Practical issues in specification and estimation

“In theory, there is no difference between theory and practice. But in practice, there is.”

— Benjamin Brewster

"An ounce of practice is generally worth more than a ton of theory.

— E.F. Schumacher

Theory and practice

Chapters @ref(chapter-2) and @ref(chapter-3) presented a conceptual framework (a theory of behavior) and the necessary apparatus (based on probability theory) to implement the conceptual framework. This theoretical introduction was necessary to begin work from a solid foundation, and it provides an intuitive and elegant framework to study decision-making, and a powerful one too; Daniel McFadden was awarded the Sveriges Riksbank Prize in Economic Sciences (Nobel Prize) for his contributions to random utility modelling.

Although not described in detail in previous chapters, it is worthwhile to dwell for a moment on the history of the development of the logit model as a random utility model.

In his Nobel Lecture, McFadden [-@McFadden2001economic] recounts the path that led to the development of random utility models for discrete choices. Like most important discoveries, it is a meandering path. It began early in the 20th century with a theory for economic behavior (i.e., utility) that considered heterogeneous preferences that in practice were difficult to verify empirically because of data limitations. Indeed, studies before the 1960s mostly considered aggregated demand with representative agents (i.e., archetypical consumers) to accommodate this limitation in data availability. It was only when individual-level data became more widely collected and within reach of researchers that it became possible to pay attention to the behavior of individual agents.

While economists were busy with models of aggregated demand, researchers in psychometrics and mathematical psychology, chiefly L.L Thurstone and R.D. Luce, were busy providing the technical basis for modelling what Thurstone termed Comparative Judgement (in the sense of making a decision or forming an opinion). In particular, Luce introduced the axiom of Independence of Irrelevant Alternatives (discussed in Chapter @ref(chapter-3)). According to McFadden (2001, p. 353), this axiom “simplified experimental collection of choice data by allowing multinomial choice probabilities to be inferred from binomial choice experiments.” J. Marschak was the first to introduce the work of Thurstone to econometrics in 1960, and also coined the term Random Utility Maximizing (RUM) that eventually prevailed over the comparative judgement terminology of Thurstone. McFadden’s early contributions to this body of research was developing an econometric version of Luce’s model, with strict (i.e., systematic) utilities specified as functions of the attributes of the alternatives. This allowed a research to link unobserved preference heterogeneity to a fully consistent description of the distribution of demand. Since the 1970s, discrete choice analysis has been a burgeoning area of research with a plethora of applications in economics, marketing, and travel behavior, among many other disciplines.

This brief story neatly illustrates the complex interplay between theory and practice.

Early attempts to study demand were limited due to practical considerations (i.e., the absence of data at the individual level). Once appropriate data became available, new studies continued to push the theoretical envelope. Indeed, theoretical questions have continued to inspire newer way to collect data and novel methods, and these in turn have helped us to refine our understanding of behavior. See as an example the work on decision-making in social situations [@Akerlof1997social; @Axhausen2005social; @Paez2007social] which inspired the use of new data sources [e.g., @Carrasco2007social @Axhausen2008social; @Scott2012social; @Chen2016social] as well as novel modelling approaches [e.g., @Dugundji2005discrete; @Dugundji2013social; @Kamargianni2014social] and empirical work [e.g., @vandenBerg2009social; @Goetzke2011bicycle; @Matous2017social].

Now that the preceding chapters have armed us with the theory and basic concepts to implement random utility models, it is proper that we turn our attention to the practical aspects of modelling. The best way to ensure that the concepts take hold, in my view, is to get your hands on a dataset and struggle with the practicalities of cleaning and organizing data, specifying the utility functions (a task that is more art than science), and estimating models. These skills are mostly transferable to other modelling techniques, so we will begin by applying them to the most fundamental of discrete choice models, namely the multinomial logit.

How to use this note

Remember that the source for the document you are reading is an R Notebook. Throughout the notes, you will find examples of code in segments of text called chunks. This is an example of a chunk:

print("Hats off to you, Prof. McFadden")
[1] "Hats off to you, Prof. McFadden"

If you are working with the Notebook version of the document, you can run the code by clicking the ‘play’ icon on the top right corner of the chunk. If you are reading the web-book version of the document, you will often see that the code has already been executed. You can still try it by copying and pasting into your R or RStudio console.

Learning objectives

In this practice, you will learn about:

  1. Specification of utility functions.
  2. Maximum likelihood estimation.
  3. Estimation of multinomial logit models.
  4. McFadden’s \(\rho^2\)
  5. The likelihood ratio test.

Suggested readings

  • Ben-Akiva, M. Lerman, [-@Benakiva1985discrete] Discrete Choice Analysis: Theory and Applications to Travel Demand, Chapters 4 and 5, MIT Press.
  • Hensher, D.A., Rose, J.M., Greene, W.H [-@hensher2005applied] Applied Choice Analysis: A Primer, Chapter 10, Cambridge University Press.
  • Ortuzar JD, Willumsen LG [-@Ortuzar2011modelling] Modelling Transport, Fourth Edition, Chapter 8, John Wiley and Sons.
  • Train [-@Train2009discrete] Discrete Choice Methods with Simulation, Second Edition, Chapter 3, Cambridge University Press.

Preliminaries

Load the packages used in this section:

library(kableExtra)
library(mlogit)
library(plotly)
library(tidyverse)

Load the dataset used in this section:

load("Commute Mac Wide.RData")

The anatomy of utility functions

At the end Chapter @ref(chapter-3) we took, for the first time, a closer look at the systematic utilities of discrete choice models. It is useful to think about the anatomy of a typical systematic utility function. Previously, we said that some variables vary across utility functions; these are typically the attributes that describe the various alternatives (e.g., their level of service and cost). The variables that describe the decision-maker do not vary by alternative. This has implications, as seen before, for how the variables are entered into the functions. Since the model works on the basis of differences between utilities, the attributes must actually measure different levels of something or vanish.

We will describe the utilities in terms of the way different variables are introduced in the utility functions. As before, we will assume that the location parameters of the distribution are absorbed by \(J-1\) utility functions (where \(J\) is the number of alternatives) in the form of alternative specific constants.

Consider first variables that vary across alternatives. These variables can have a generic coefficient or they can have alternative-specific coefficients, as seen here:

\[ \begin{array}{l} V_{i1} =\\ V_{i2} =\\ V_{i3} =\\ \end{array} \overbrace{ \begin{array}{lll} 0 & +0 & +0\\ 0 & +\mu_2 & +0 \\ 0 & +0 & +\mu_3\\ \end{array} }^\text{alternative specific constants} \underbrace{\begin{array}{lll} +\beta_1x_{i1}\\ +\beta_1x_{i2}\\ +\beta_1x_{i3}\\ \end{array} }_{\text{alternative vars. with generic coefficients}} \overbrace{\begin{array}{lll} +\delta_1w_{i1} & +0 & +0\\ +0 & +\delta_2w_{i2} & +0\\ +0 & +0 & +\delta_3w_{i3}\\ \end{array} }^{\text{alternative vars. with specific coefficients}} \]

In many cases it is sensible to have generic coefficients. For instance, if the variable is cost, we might assume that one dollar is valued equally irrespective of the which alternative it is spent on. In other cases, the use of alternative-specific coefficients might be informative. For instance, a consistent finding in the literature is that time waiting for a bus is perceived as being more expensive than time actually on-board and traveling in the bus. Occasionally, as well, an attribute might be specific to an alternative: for instance, waiting time is often implicitly zero for travel by car and active modes of transportation (i.e., walking and cycling).

The differences of the utilities are as follows:

\[ \begin{array}{lll} V_{i2}-V_{i1}=&(\mu_2 - 0) & + &\beta_1(x_{i2} - x_{i1}) & + &(\delta_2w_{i2} - \delta_1w_{i1})\\ V_{i3}-V_{i1}=&(\mu_3 - 0) & + &\beta_1(x_{i2} - x_{i1}) & + &(\delta_3w_{i3} - \delta_1w_{i1})\\ V_{i3}-V_{i2}=&(\mu_3- \mu_2) & + &\beta_1(x_{i2} - x_{i1}) & + &(\delta_3w_{i3} - \delta_2w_{i2})\\ \end{array} \]

Variables that vary across individuals but not by alternative can be introduced with alternative-specific coefficients:

$$ \begin{array}{l} V_{i1} =\ V_{i2} =\ V_{i3} =\ \end{array}
^ _{}

^{} _ $$

Following the example above, the differences of utilities are:

\[ \begin{array}{lll} V_{i2}-V_{i1}=&(\mu_2 - 0) & + &\beta_1(x_{i2} - x_{i1}) & + &(\delta_2w_{i2} - \delta_1w_{i1}) & + &(\gamma_2 - 0)z_i\\ V_{i3}-V_{i1}=&(\mu_3 - 0) & + &\beta_1(x_{i2} - x_{i1}) & + &(\delta_3w_{i3} - \delta_1w_{i1}) & + &(\gamma_3 - 0)z_i\\ V_{i3}-V_{i2}=&(\mu_3- \mu_2) & + &\beta_1(x_{i2} - x_{i1}) & + &(\delta_3w_{i3} - \delta_2w_{i2}) & + &(\gamma_3 - \gamma_2)z_i\\ \end{array} \]

A different way of introducing individual-level variables is as part of an expansion of some coefficients, for example:

$$

\begin{array}{l} V_{i1} =\ V_{i2} =\ V_{i3} =\ \end{array}
^ _{} ^{} $$

The above expands to:

$$

\begin{array}{l} V_{i1} =\ V_{i2} =\ V_{i3} =\ \end{array}
^ _{} ^{} $$

And so the differences in utilities are:

\[ \begin{array}{lll} V_{i2}-V_{i1}=&(\mu_2 - 0) & + &\beta_{11}(x_{i2} - x_{i1}) & + &\beta_{11}(z_ix_{i2} - z_ix_{i1}) & + &(\delta_2w_{i2} - \delta_1w_{i1})\\ V_{i3}-V_{i1}=&(\mu_3 - 0) & + &\beta_{11}(x_{i2} - x_{i1}) & + &\beta_{11}(z_ix_{i3} - z_ix_{i1}) & + &(\delta_3w_{i3} - \delta_1w_{i1})\\ V_{i3}-V_{i2}=&(\mu_3- \mu_2) & + &\beta_{11}(x_{i2} - x_{i1}) & + &\beta_{11}(z_ix_{i3} - z_ix_{i2}) & + &(\delta_3w_{i3} - \delta_2w_{i2})\\ \end{array} \]

Understanding the anatomy of utility functions is essential to properly specify and estimate models.

Example: Specifying the utility functions

We will now proceed to work with a practical example, using the dataset that you encountered before in Chapter @ref(chapter-1). This dataset contains information on various modes of transportation used by people commuting to McMaster University in Canada [@Whalen2013]. The dataset was loaded above as part of the preliminaries of this chapter. We can begin by exploring the data. Please note that this is the same dataset that you used in Chapter @ref(chapter-1), but not the same file. For convenience, the dataset was pre-processed. The contents of the dataframe can be quickly seen by means of the function head(). This function will display the first few top rows of the dataframe:

head(mc_commute, 8)

As you can see, the dataframe is in wide format, meaning that each row represents one decision-maker and the information about the choice situations is spread: for instance, there is one variable for travel time, but it appears in four columns, one for each of the alternatives (“Cycle”, “Walk”, “HSR”, and “Car”). The package mlogit works with long tables, where each row is a choice situation. Since wide tables are more common, the package includes a utility function to reshape the table. This is used below to convert our wide table into a long table. Notice that we need to indicate which variables are varying, meaning that they vary by alternative. In our wide table, there are four variables that vary by alternative: travel time (time), access time (access: the time needed to reach an HSR bus stop), waiting time (wait: time spent waiting for an HSR bus), and number of transfers when using HSR (transfer). The latter three variables are specific to HSR and therefore are set to zero for the modes “Car”, Cycle“, and”Walk".

mc_commute_long <- mlogit.data(mc_commute, shape = "wide", choice = "choice", varying = 7:22)

If we examine the long table, we see that instead of each row being an individual, each row is a choice situation:

head(mc_commute_long, 8)

Since there are four alternatives in this case, each row corresponds to the choice situation for an alternative for an individual. We notice that the row names now have the format #.Alt, where # is the number of the decision-maker and Alt is the name of the alternative. In this way the first four rows of the table correspond to the first decision-maker who, faced with four alternatives, chose HSR (public transportation) - as recorded in the column choice. The next four rows correspond to the second decision-maker in the sample (who also chose HSR), and so on, four rows per decision-maker. More generally, there will be \(J\) rows per decision-maker. When an attribute is missing, this means that the alternative was not available to the decision-maker. For example:

select(mc_commute_long, time) %>% head(8)

Here we see that the modes “Car” and “Cycle” were not available to decision-maker 1, and “Cycle” was not available to decision-maker 2.

The first step towards developing a choice model is to specify the utility functions for the desired model. The package mlogit uses the package mFormula to create the functions. This package creates objects that build upon the Formula package for multi-component formulas. As seen above, utility functions can potentially have multiple components, so the functionality to build formulas in mFormula and Formula is quite useful.

Formulas for the mlogit package are defined using three parts:

\[ \text{choice} \sim \text{alternative specific vars with generic coefficients }|\text{ individual specific vars }|\text{ alternative specific vars with specific coefficients} \]

If we list all columns in the dataframe, we can see what variables are available for this analysis:

colnames(mc_commute_long)
 [1] "id"                        "choice"                    "available.Cycle"           "available.Walk"           
 [5] "available.HSR"             "available.Car"             "parking"                   "vehind"                   
 [9] "gender"                    "age"                       "shared"                    "family"                   
[13] "child"                     "street_density"            "sidewalk_density"          "LAT"                      
[17] "LONG"                      "PersonalVehComf_SD"        "PersonalVehComf_D"         "PersonalVehComf_A"        
[21] "PersonalVehComf_SA"        "Fun_SD"                    "Fun_D"                     "Fun_A"                    
[25] "Fun_SA"                    "ActiveNeigh_SD"            "ActiveNeigh_D"             "ActiveNeigh_A"            
[29] "ActiveNeigh_SA"            "UsefulTrans_SD"            "UsefulTrans_D"             "UsefulTrans_A"            
[33] "UsefulTrans_SA"            "BusComf_SD"                "BusComf_D"                 "BusComf_A"                
[37] "BusComf_SA"                "TravelAlone_SD"            "TravelAlone_D"             "TravelAlone_A"            
[41] "TravelAlone_SA"            "Shelters_SD"               "Shelters_D"                "Shelters_A"               
[45] "Shelters_SA"               "Community_SD"              "Community_D"               "Community_A"              
[49] "Community_SA"              "personal_veh_comfortable"  "getting_there_fun"         "like_active_neighborhood" 
[53] "commute_useful_transition" "buses_comfortable"         "prefer_travel_alone"       "shelter_good_quality"     
[57] "sense_community"           "numna"                     "alt"                       "time"                     
[61] "access"                    "wait"                      "transfer"                  "chid"                     

Besides identifier variables id and chid, and the variable for choice, we see that several variables are specific to the individual decision-makers. These are parking (availability of a parking pass), vehind (whether the decision-maker had individual access to a private vehicle), gender, age, shared (living in shared accommodations away from the family home), family (living at the family home), and child (respondent was responsible for at least one minors in the household). Furthermore, some variables relate to the physical environment of the place of residence (street_density and sidewalk_density), in addition to the coordinates of the place of residence (geocoded to the nearest major intersection or postal code centroid). One variable is alternative specific, namely time (travel time in minutes). And, as noted before, three variables are specific to public transportation, namely access (access time to public transportation in minutes), wait (waiting time in minutes), and transfer (number of transfers when traveling by public transportation).

We can begin by defining a very simple formula that considers only travel time. We will save this object as f1:

f1 <- mFormula(choice ~ time)

The function model.matrix allows us to see how the formula is applied to the data (we use head() to display only the top rows of the model matrix):

head(model.matrix(f1, mc_commute_long, 8))
       Cycle:(intercept) HSR:(intercept) Walk:(intercept)     time
1.HSR                  0               1                0  5.00000
1.Walk                 0               0                1 21.31439
2.Car                  0               0                0  2.00000
2.HSR                  0               1                0 10.00000
2.Walk                 0               0                1 12.78863
3.Car                  0               0                0  4.00000

We can see that the formula includes by default the alternative specific coefficients, in this case using car as the reference alternative. The corresponding utility functions are as follows:

\[ \begin{array}{l} V_{i\text{Cycle}} =\\ V_{i\text{Walk}} =\\ V_{i\text{HSR}} =\\ V_{i\text{HSR}} =\\ \end{array} \overbrace{ \begin{array}{lll} 0 & +0 & +0\\ \mu_{\text{Walk}} & +0 & +0\\ 0 & +\mu_{\text{HSR}} & +0 \\ 0 & +0 & +\mu_{\text{Car}}\\ \end{array} }^\text{alternative specific constants} \underbrace{\begin{array}{lll} +\beta_1\text{time}_{i\text{Cycle}}\\ +\beta_1\text{time}_{i\text{Walk}}\\ +\beta_1\text{time}_{i\text{HSR}}\\ +\beta_1\text{time}_{i\text{Car}}\\ \end{array} }_{\text{alternative vars. with generic coefficients}} \]

Define now a formula with an individual-specific variable, say sidewalk density at the place of residence, and call it f2:

f2 <- mFormula(choice ~ time | sidewalk_density)

The model matrix is now:

head(model.matrix(f2, mc_commute_long))
       Cycle:(intercept) HSR:(intercept) Walk:(intercept)     time Cycle:sidewalk_density HSR:sidewalk_density Walk:sidewalk_density
1.HSR                  0               1                0  5.00000                      0             22.63322               0.00000
1.Walk                 0               0                1 21.31439                      0              0.00000              22.63322
2.Car                  0               0                0  2.00000                      0              0.00000               0.00000
2.HSR                  0               1                0 10.00000                      0             39.64003               0.00000
2.Walk                 0               0                1 12.78863                      0              0.00000              39.64003
3.Car                  0               0                0  4.00000                      0              0.00000               0.00000

And the utility functions are therefore:

\[ \begin{array}{l} V_{i\text{Cycle}} =\\ V_{i\text{Walk}} =\\ V_{i\text{HSR}} =\\ V_{i\text{HSR}} =\\ \end{array} \overbrace{ \begin{array}{lll} 0 & +0 & +0\\ \mu_{\text{Walk}} & +0 & +0\\ 0 & +\mu_{\text{HSR}} & +0 \\ 0 & +0 & +\mu_{\text{Car}}\\ \end{array} }^\text{alternative specific constants} \underbrace{\begin{array}{lll} +\beta_1\text{time}_{i\text{Cycle}}\\ +\beta_1\text{time}_{i\text{Walk}}\\ +\beta_1\text{time}_{i\text{HSR}}\\ +\beta_1\text{time}_{i\text{Car}}\\ \end{array} }_{\text{alternative vars. with generic coefficients}} \overbrace{ \begin{array}{lll} 0 & +0 & +0\\ \gamma_{1}\text{swd}_{i} & +0 & +0\\ 0 & + \gamma_{2}\text{swd}_{i} & +0 \\ 0 & +0 & +\gamma_{3}\text{swd}_{i}\\ \end{array} }^\text{individual vars with specific coefficients} \]

Here, we try a different formula, where time has alternative-specific instead of generic coefficients, and call it f3:

f3 <- mFormula(choice ~ 0 | sidewalk_density | time)

Note that, since we do not define other alternative-specific variables with generic coefficients, we have to explicitly state that there are 0 such variables!

This formula leads to the following model matrix:

head(model.matrix(f3, mc_commute_long))
       Cycle:(intercept) HSR:(intercept) Walk:(intercept) Cycle:sidewalk_density HSR:sidewalk_density Walk:sidewalk_density Car:time
1.HSR                  0               1                0                      0             22.63322               0.00000        0
1.Walk                 0               0                1                      0              0.00000              22.63322        0
2.Car                  0               0                0                      0              0.00000               0.00000        2
2.HSR                  0               1                0                      0             39.64003               0.00000        0
2.Walk                 0               0                1                      0              0.00000              39.64003        0
3.Car                  0               0                0                      0              0.00000               0.00000        4
       Cycle:time HSR:time Walk:time
1.HSR           0        5   0.00000
1.Walk          0        0  21.31439
2.Car           0        0   0.00000
2.HSR           0       10   0.00000
2.Walk          0        0  12.78863
3.Car           0        0   0.00000

The utility functions for this are:

$$ \begin{array}{l} V_{i} =\ V_{i} =\ V_{i} =\ V_{i} =\ \end{array}
^ _ ^{}

$$

Given the utility functions, the logit probabilities for each alternative are:

\[ \begin{array}{l} P(\text{Cycle}) = \frac{e^{V_{\text{Cycle}}}}{e^{V_{\text{Cycle}}}+e^{V_{\text{Walk}}}+e^{V_{\text{HSR}}}+e^{V_{\text{Car}}}}\\ P(\text{Walk}) = \frac{e^{V_{\text{Walk}}}}{e^{V_{\text{Cycle}}}+e^{V_{\text{Walk}}}+e^{V_{\text{HSR}}}+e^{V_{\text{Car}}}}\\ P(\text{HSR}) = \frac{e^{V_{\text{Cycle}}}}{e^{V_{\text{Cycle}}}+e^{V_{\text{Walk}}}+e^{V_{\text{HSR}}}+e^{V_{\text{Car}}}}\\ P(\text{Car}) =1 - P(\text{Cycle}) - P(\text{Walk}) - P(\text{HSR})\\ \end{array} \]

The utility functions depend on the data but also on the coefficients, which we do not know a priori. Rather, these must be retrieved from the sample, as discussed next.

Estimation

Before we can calculate the choice probabilities, we need to somehow obtain coefficients for the utility functions. The process to do so is called estimation, and it involves the use of a statistical sample.

To estimate the coefficients of a model we need to define a criterion that we wish to satisfy with our choice of coefficients. Estimates can take an infinite number of values, after all, so our criterion must be optimal in some sense - in this way, once that we estimate the coefficients we can be satisfied that they are the best that we can obtain for the model under considerations, given then inputs.

A common criterion used to estimate discrete choice models is the likelihood. So what is this likelihood? Previously we encountered probability distribution functions. These functions were defined by parameters (such as the location parameter and the dispersion parameter). Given the parameters, it is possible to calculate the probability of values for a variable \(x\). A likelihood function is a similar concept, except that whereas in the probability functions the parameters were given, in a likelihood function the data are given and the parameters need to be obtained from the function.

The relevant likelihood function for the multinomial logit model is as follows: \[ L = \prod_{i=n}^N\prod_{j=1}^J P_{ij}^{y_{ij}} \] where \(P_{ij}\) is the probability of decision-maker \(i\) selecting alternative \(j\) and \(y_{ij}\) is an indicator variable that takes the value of \(1\) if individual \(i\) chose alternative \(j\) and \(0\) otherwise. The effect of the indicator variable is to turn the probabilities on and off, since \(P^0 = 1\) and \(P^1 = P\). Notice that the likelihood function is bounded between 0 and 1, but in the case of the logit model it is never exactly zero nor one, since the logit probabilities never take any of those exact values.

We can explore the behavior of the likelihood function by means of a simple example. Consider a binomial logit mode, that is, a model with only two alternatives in the choice set. The likelihood function of this model is as follows:

\[ L = \prod_{i=n}^N P_{iA}^{y_{iA}}P_{iB}^{y_{iB}} = \prod_{i=n}^N \Bigg(\frac{e^{V_{iA}}}{e^{V_{iA}} + e^{V_{iB}}}\Bigg)^{y_{iA}} \Bigg(\frac{e^{V_{iB}}}{e^{V_{iA}} + e^{V_{iB}}}\Bigg)^{y_{iB}} \]

The utility functions \(V_{iA}\) and \(V_{iB}\) depend on the data, which we know (since we have a statistical sample), and the coefficients, which we do not know.

For the example, we have the following toy sample with six individuals:

Individual Choice yiA yiB xiA xiB
1 A 1 0 5 4
2 A 1 0 2 5
3 B 0 1 5 2
4 A 1 0 1 6
5 B 0 1 4 1
6 B 0 1 3 4

Based on this sample, we can specify the utility functions in this fashion:

\[ \begin{array}{l} V_{iA} = 0 &+& \beta x_{iA}\\ V_{iB} = \mu &+& \beta x_{iB}\\ \end{array} \]

These utility functions are very similar to the first set of utility functions that we defined in the preceding section for the case of mode choice.

Next, the likelihood function for this toy sample can be writen as a function of \(\mu\) and \(\beta\). In this way, it is possible to calculate an initial value of the likelihood function bu setting \(\mu\) and \(\beta\) to zero. We will call this “Experiment 1”:

mu <- 0
beta <- 0

P1A_1 <- (exp(beta * ts$xiA[1])/(exp(beta * ts$xiA[1]) + exp(mu + beta * ts$xiB[1])))
P1B_1 <- (exp(mu + beta * ts$xiB[1])/(exp(beta * ts$xiA[1]) + exp(mu + beta * ts$xiB[1])))
P2A_1 <- (exp(beta * ts$xiA[2])/(exp(beta * ts$xiA[2]) + exp(mu + beta * ts$xiB[2])))
P2B_1 <- (exp(mu + beta * ts$xiB[2])/(exp(beta * ts$xiA[2]) + exp(mu + beta * ts$xiB[2])))
P3A_1 <- (exp(beta * ts$xiA[3])/(exp(beta * ts$xiA[3]) + exp(mu + beta * ts$xiB[3])))
P3B_1 <- (exp(mu + beta * ts$xiB[3])/(exp(beta * ts$xiA[3]) + exp(mu + beta * ts$xiB[3])))
P4A_1 <- (exp(beta * ts$xiA[4])/(exp(beta * ts$xiA[4]) + exp(mu + beta * ts$xiB[4])))
P4B_1 <- (exp(mu + beta * ts$xiB[4])/(exp(beta * ts$xiA[4]) + exp(mu + beta * ts$xiB[4])))
P5A_1 <- (exp(beta * ts$xiA[5])/(exp(beta * ts$xiA[5]) + exp(mu + beta * ts$xiB[5])))
P5B_1 <- (exp(mu + beta * ts$xiB[5])/(exp(beta * ts$xiA[5]) + exp(mu + beta * ts$xiB[5])))
P6A_1 <- (exp(beta * ts$xiA[6])/(exp(beta * ts$xiA[6]) + exp(mu + beta * ts$xiB[6])))
P6B_1 <- (exp(mu + beta * ts$xiB[6])/(exp(beta * ts$xiA[6]) + exp(mu + beta * ts$xiB[6])))
  
L <-  P1A_1^ts$yiA[1] * P1B_1^ts$yiB[1] * 
  P2A_1^ts$yiA[2] * P2B_1^ts$yiB[2] * 
  P3A_1^ts$yiA[3] * P3B_1^ts$yiB[3] * 
  P4A_1^ts$yiA[4] * P4B_1^ts$yiB[4] * 
  P5A_1^ts$yiA[5] * P5B_1^ts$yiB[5] * 
  P6A_1^ts$yiA[6] * P6B_1^ts$yiB[6] 

# Create data frame to tabulate results:
df <- data.frame(Individual = c(1, 2, 3, 4, 5, 6),
                 Choice = c("A", "A", "B", "A", "B", "B"),
                 PA = c(P1A_1, P2A_1, P3A_1, P4A_1, P5A_1, P6A_1),
                 PB = c(P1B_1, P2B_1, P3B_1, P4B_1, P5B_1, P6B_1))

kable(df, "html", digits = 4, align = "c") %>%
  kable_styling(bootstrap_options = c("striped", "hover")) %>%
  footnote(general = paste("The value of the likelihood function is ", round(L, digits = 4)))
Individual Choice PA PB
1 A 0.5 0.5
2 A 0.5 0.5
3 B 0.5 0.5
4 A 0.5 0.5
5 B 0.5 0.5
6 B 0.5 0.5
Note:
The value of the likelihood function is 0.0156

As you can see, that the logit probabilities when all coefficients are zero is \(0.5\). By setting the coefficients to zero we have defined what is called a null model. Since the variables are set to zero, this model has no useful information to estimate the probability, and therefore it assigns equal probabilities to all alternatives. The value of the likelihood function is a relatively small (positive) number (remember, the function is bounded between zero and one).

Now, we can experiment with the coefficients, by giving them different values as follows (call this “Experiment 2”):

mu <- 0.5 # -0.5
beta <- -0.5 # -0.5

P1A_2 <- (exp(beta * ts$xiA[1])/(exp(beta * ts$xiA[1]) + exp(mu + beta * ts$xiB[1])))
P1B_2 <- (exp(mu + beta * ts$xiB[1])/(exp(beta * ts$xiA[1]) + exp(mu + beta * ts$xiB[1])))
P2A_2 <- (exp(beta * ts$xiA[2])/(exp(beta * ts$xiA[2]) + exp(mu + beta * ts$xiB[2])))
P2B_2 <- (exp(mu + beta * ts$xiB[2])/(exp(beta * ts$xiA[2]) + exp(mu + beta * ts$xiB[2])))
P3A_2 <- (exp(beta * ts$xiA[3])/(exp(beta * ts$xiA[3]) + exp(mu + beta * ts$xiB[3])))
P3B_2 <- (exp(mu + beta * ts$xiB[3])/(exp(beta * ts$xiA[3]) + exp(mu + beta * ts$xiB[3])))
P4A_2 <- (exp(beta * ts$xiA[4])/(exp(beta * ts$xiA[4]) + exp(mu + beta * ts$xiB[4])))
P4B_2 <- (exp(mu + beta * ts$xiB[4])/(exp(beta * ts$xiA[4]) + exp(mu + beta * ts$xiB[4])))
P5A_2 <- (exp(beta * ts$xiA[5])/(exp(beta * ts$xiA[5]) + exp(mu + beta * ts$xiB[5])))
P5B_2 <- (exp(mu + beta * ts$xiB[5])/(exp(beta * ts$xiA[5]) + exp(mu + beta * ts$xiB[5])))
P6A_2 <- (exp(beta * ts$xiA[6])/(exp(beta * ts$xiA[6]) + exp(mu + beta * ts$xiB[6])))
P6B_2 <- (exp(mu + beta * ts$xiB[6])/(exp(beta * ts$xiA[6]) + exp(mu + beta * ts$xiB[6])))
  
L <-  P1A_2^ts$yiA[1] * P1B_2^ts$yiB[1] * 
  P2A_2^ts$yiA[2] * P2B_2^ts$yiB[2] * 
  P3A_2^ts$yiA[3] * P3B_2^ts$yiB[3] * 
  P4A_2^ts$yiA[4] * P4B_2^ts$yiB[4] * 
  P5A_2^ts$yiA[5] * P5B_2^ts$yiB[5] * 
  P6A_2^ts$yiA[6] * P6B_2^ts$yiB[6] 

# Create data frame to tabulate results:
df <- data.frame(Individual = c(1, 2, 3, 4, 5, 6),
                 Choice = c("A", "A", "B", "A", "B", "B"),
                 PA = c(P1A_2, P2A_2, P3A_2, P4A_2, P5A_2, P6A_2),
                 PB = c(P1B_2, P2B_2, P3B_2, P4B_2, P5B_2, P6B_2))

kable(df, "html", digits = 4, align = "c") %>%
  kable_styling(bootstrap_options = c("striped", "hover")) %>%
  footnote(general = paste("The value of the likelihood function is ", round(L, digits = 4)))
Individual Choice PA PB
1 A 0.2689 0.7311
2 A 0.7311 0.2689
3 B 0.1192 0.8808
4 A 0.8808 0.1192
5 B 0.1192 0.8808
6 B 0.5000 0.5000
Note:
The value of the likelihood function is 0.0672

Notice how changing the coefficients has two effects, as you would expect: the probabilities change and the value of the likelihood function changes too. Inspect the probabilities and the value of the likelihood function with the new coefficients. What do you notice?

If you are working with the R Notebook, at this point you can try changing the coefficients. Can you improve the value of the likelihood function, or maybe even make it worse?

The likelihood function can be plotted as shown below. If you hover over the plot, you can see how the value of the likelihood changes as a function of \(\mu\) and \(\beta\):

From Figure @ref(fig:fig-evi-distribution) we can see that the approximate values of the coefficients that maximize the likelihood function are \(\mu=0.10\) and \(\beta=-0.65\). If we use these coefficients to calculate the logit probabilities, we can compare to the probabilities of Experiments 1 and 2:

# Approximate values that maximize the likelihood function.
mu <- 0.10
beta <- -0.65

P1A_3 <- (exp(beta * ts$xiA[1])/(exp(beta * ts$xiA[1]) + exp(mu + beta * ts$xiB[1])))
P1B_3 <- (exp(mu + beta * ts$xiB[1])/(exp(beta * ts$xiA[1]) + exp(mu + beta * ts$xiB[1])))
P2A_3 <- (exp(beta * ts$xiA[2])/(exp(beta * ts$xiA[2]) + exp(mu + beta * ts$xiB[2])))
P2B_3 <- (exp(mu + beta * ts$xiB[2])/(exp(beta * ts$xiA[2]) + exp(mu + beta * ts$xiB[2])))
P3A_3 <- (exp(beta * ts$xiA[3])/(exp(beta * ts$xiA[3]) + exp(mu + beta * ts$xiB[3])))
P3B_3 <- (exp(mu + beta * ts$xiB[3])/(exp(beta * ts$xiA[3]) + exp(mu + beta * ts$xiB[3])))
P4A_3 <- (exp(beta * ts$xiA[4])/(exp(beta * ts$xiA[4]) + exp(mu + beta * ts$xiB[4])))
P4B_3 <- (exp(mu + beta * ts$xiB[4])/(exp(beta * ts$xiA[4]) + exp(mu + beta * ts$xiB[4])))
P5A_3 <- (exp(beta * ts$xiA[5])/(exp(beta * ts$xiA[5]) + exp(mu + beta * ts$xiB[5])))
P5B_3 <- (exp(mu + beta * ts$xiB[5])/(exp(beta * ts$xiA[5]) + exp(mu + beta * ts$xiB[5])))
P6A_3 <- (exp(beta * ts$xiA[6])/(exp(beta * ts$xiA[6]) + exp(mu + beta * ts$xiB[6])))
P6B_3 <- (exp(mu + beta * ts$xiB[6])/(exp(beta * ts$xiA[6]) + exp(mu + beta * ts$xiB[6])))
  
L <-  P1A_3^ts$yiA[1] * P1B_3^ts$yiB[1] * 
  P2A_3^ts$yiA[2] * P2B_3^ts$yiB[2] * 
  P3A_3^ts$yiA[3] * P3B_3^ts$yiB[3] * 
  P4A_3^ts$yiA[4] * P4B_3^ts$yiB[4] * 
  P5A_3^ts$yiA[5] * P5B_3^ts$yiB[5] * 
  P6A_3^ts$yiA[6] * P6B_3^ts$yiB[6] 

# Create data frame to tabulate results:
df <- data.frame(Individual = c(1, 2, 3, 4, 5, 6),
                 Choice = c("A", "A", "B", "A", "B", "B"),
                 PA_1 = c(P1A_1, P2A_1, P3A_1, P4A_1, P5A_1, P6A_1),
                 PB_1 = c(P1B_1, P2B_1, P3B_1, P4B_1, P5B_1, P6B_1),
                 PA_1 = c(P1A_2, P2A_2, P3A_2, P4A_2, P5A_2, P6A_2),
                 PB_1 = c(P1B_2, P2B_2, P3B_2, P4B_2, P5B_2, P6B_2),
                 PA_1 = c(P1A_3, P2A_3, P3A_3, P4A_3, P5A_3, P6A_3),
                 PB_1 = c(P1B_3, P2B_3, P3B_3, P4B_3, P5B_3, P6B_3))

kable(df, "html", digits = 4, 
      col.names = c("Individual", "Choice", "PA", "PB", "PA", "PB", "PA", "PB"),
      align = "c") %>%
  kable_styling(bootstrap_options = c("striped", "hover")) %>%
  add_header_above(c(" " = 1, " " = 1, "Experiment 1" = 2, "Experiment 2" = 2, "Approx Max Likelihood" = 2))
Experiment 1
Experiment 2
Approx Max Likelihood
Individual Choice PA PB PA PB PA PB
1 A 0.5 0.5 0.2689 0.7311 0.3208 0.6792
2 A 0.5 0.5 0.7311 0.2689 0.8641 0.1359
3 B 0.5 0.5 0.1192 0.8808 0.1141 0.8859
4 A 0.5 0.5 0.8808 0.1192 0.9589 0.0411
5 B 0.5 0.5 0.1192 0.8808 0.1141 0.8859
6 B 0.5 0.5 0.5000 0.5000 0.6341 0.3659

Maximizing the likelihood is a useful criterion to estimate the coefficients of the models, since this criterion provides the optimal probabilities of the right alternative being chosen. Mind you, this does not necessarily mean that those probabilities will be high - however, we can be certain that they will be the best for the model under consideration for the sample given.

In this toy example we “solved” the problem of maximizing the likelihood by hand. This is rather difficult, unfeasible even, in most applied situations with large samples and/or more than one variable. Fortunately, there are a number of numerical algorithms that can be used to maximize the likelihood. We will not discuss this in detail, but interested readers can consult Train [@Train2009discrete; Section 3.7] for details. The mlogit package imports the package maxLik [@Henningsen2011maxlik], which implements canonical algorithms including Newton-Raphson, the Berndt–Hall–Hall–Hausman (or BHHH), and the Broyden–Fletcher–Goldfarb–Shanno (or BFGS) algorithm.

In practice, the algorithms above maximize not the likelihood function, but a transformation thereof, called the log-likelihood, which is obtained by taking the natural logarithm of the function, to give:

\[ l = \sum_{i=n}^N\sum_{j=1}^J y_{ij}log(P_{ij}) \] Since the likelihood function is bound between zero and one, the log-likelihood is bound between minus infinity and zero. The value of the maximized log-likelihood function provides a useful diagnostic to compare models, since higher values are indicative of a better model. Several statistical tests (such as the likelihood ratio) can be used to test the hypothesis that a model is a significant improvement over other, and are thus useful for model selection purposes. However, before discussing model diagnostics, we will see how multinomial logit models are estimated using mlogit.

Example: A logit model of mode choice

Coming back to the transportation mode choice dataset, we had already defined some formulas (i.e., utility functions) that we can use to estimate a model.

The function to estimate a model is mlogit(). This function requires at least two arguments: an mFormula object and a dataset. We can verify that the formulas we created above are of this class:

class(f1)
[1] "mFormula" "Formula"  "formula" 
class(f2)
[1] "mFormula" "Formula"  "formula" 
class(f3)
[1] "mFormula" "Formula"  "formula" 

The value (output) of the function can be named and saved to an object for further analysis or for further processing, post-estimation. Begin by estimating a model using the simplest of our formulas:

model1 <- mlogit(f1, mc_commute_long)
summary(model1)

Call:
mlogit(formula = choice ~ time, data = mc_commute_long, method = "nr")

Frequencies of alternatives:
     Car    Cycle      HSR     Walk 
0.204364 0.034182 0.244364 0.517091 

nr method
5 iterations, 0h:0m:0s 
g'(-H)^-1g = 0.000102 
successive function values within tolerance limits 

Coefficients :
                   Estimate Std. Error z-value  Pr(>|z|)    
Cycle:(intercept)  0.366898   0.201434  1.8214   0.06854 .  
HSR:(intercept)    0.707267   0.118104  5.9885 2.118e-09 ***
Walk:(intercept)   3.212834   0.184169 17.4451 < 2.2e-16 ***
time              -0.056494   0.005685 -9.9374 < 2.2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Log-Likelihood: -768.63
McFadden R^2:  0.50323 
Likelihood ratio test : chisq = 1557.2 (p.value = < 2.22e-16)

The output of the function includes the estimated frequencies of alternatives in addition to information about the optimization procedure. For instance, the message “successive function values within tolerance limits” indicates that the algorithm converged normally.

The output also reports the estimated values of the coefficients, along with standard errors, \(z\)-values, and \(p\)-vallues. The null hypothesis associated with the coefficients is that they are zero. An analyst can reject the null hypothesis in any case, but small \(p\)-vallues indicate a low probability that the coefficient is zero - and therefore increase the confidence that by rejecting the null the analyst is not mistakenly rejecting a true zero. In the present case, with \(p\)-vallues smaller than 0.0001, the null hypothesis can be comfortably rejected for every coefficient. The small \(p\)-vallues mean that it is highly unlikely that the coefficients are zero.

This simple model includes three alternative-specific constants and one alternative-specific variable with a generic coefficient. The signs of the coefficients are informative. Since the reference mode is “Car”, the positive values of the constants indicate that, other things being equal, car is the least preferred mode, followed by cycling, HSR, and then walk (which gives the highest utility at a constant value of time). This is verified from the estimated frequencies of the modes, where we see that “Walk” is the mode of choice of 51.7% of respondents in the sample.

The negative coefficient for time indicates that time is a “cost”, in other words, the utility of travelling tends to decline with increasing travel times. This indicates that slower modes will tend to have lower utilities.

Finally, the maximized value of the log-likelihood function is reported, along with two diagnostics, McFadden R^2 (in reality \(\rho^2\)) and a likelihood ratio test. We will come back to these diagnostics below, but first, we will estimate a new model using the second formula.

model2 <- mlogit(f2, mc_commute_long)
summary(model2)

Call:
mlogit(formula = choice ~ time | sidewalk_density, data = mc_commute_long, 
    method = "nr")

Frequencies of alternatives:
     Car    Cycle      HSR     Walk 
0.204364 0.034182 0.244364 0.517091 

nr method
5 iterations, 0h:0m:0s 
g'(-H)^-1g = 0.000287 
successive function values within tolerance limits 

Coefficients :
                         Estimate Std. Error z-value  Pr(>|z|)    
Cycle:(intercept)      -0.3058620  0.4541379 -0.6735  0.500629    
HSR:(intercept)         0.6761001  0.2150745  3.1436  0.001669 ** 
Walk:(intercept)        2.3453233  0.3124706  7.5057 6.106e-14 ***
time                   -0.0562191  0.0056910 -9.8785 < 2.2e-16 ***
Cycle:sidewalk_density  0.0273085  0.0169331  1.6127  0.106803    
HSR:sidewalk_density    0.0016950  0.0085746  0.1977  0.843299    
Walk:sidewalk_density   0.0350392  0.0111749  3.1355  0.001715 ** 
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Log-Likelihood: -760.42
McFadden R^2:  0.50854 
Likelihood ratio test : chisq = 1573.7 (p.value = < 2.22e-16)

Now there is an individual-specific variable in the model (i.e., sidewalk density). Only one of those coefficients is significant at a conventional level of significance (i.e., \(p<0.05\)), and the values is positive. Since the reference is “Car”, a positive value indicates that higher sidewalk density causes the utility of walking to increase with respect to the utility of using a car. The same is not true for HSR and “Cycle”, whose coefficients for this attribute are not significantly different from “Car”.

Note that it is possible to select the reference level for the utilities when estimating the model. For example, below we re-estimate the preceding model, but now using the utility of Walk as the reference:

model2 <- mlogit(f2, mc_commute_long, reflevel = "Walk")
summary(model2)

Call:
mlogit(formula = choice ~ time | sidewalk_density, data = mc_commute_long, 
    reflevel = "Walk", method = "nr")

Frequencies of alternatives:
    Walk      Car    Cycle      HSR 
0.517091 0.204364 0.034182 0.244364 

nr method
5 iterations, 0h:0m:0s 
g'(-H)^-1g = 0.000287 
successive function values within tolerance limits 

Coefficients :
                         Estimate Std. Error z-value  Pr(>|z|)    
Car:(intercept)        -2.3453233  0.3124706 -7.5057 6.106e-14 ***
Cycle:(intercept)      -2.6511853  0.4187557 -6.3311 2.434e-10 ***
HSR:(intercept)        -1.6692232  0.2302925 -7.2483 4.221e-13 ***
time                   -0.0562191  0.0056910 -9.8785 < 2.2e-16 ***
Car:sidewalk_density   -0.0350392  0.0111749 -3.1355  0.001715 ** 
Cycle:sidewalk_density -0.0077307  0.0148205 -0.5216  0.601935    
HSR:sidewalk_density   -0.0333442  0.0084297 -3.9556 7.635e-05 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Log-Likelihood: -760.42
McFadden R^2:  0.50854 
Likelihood ratio test : chisq = 1573.7 (p.value = < 2.22e-16)

Notice that now two sidewalk coefficient are significant! While sidewalk density does not significantly change the utility of cycling with respect to walking, living in a place with high sidewalk density reduces the utility of “Car” and “HSR” with respect to walking.

The value of the maximized log-likelihood and other model diagnostics are identical irrespective of which mode is selected as a utility. In essence, the models are the same, but they provide a different perspective on how some coefficients relate to each other across alternatives.

We can visually explore how the probability of choosing different modes varies with sidewalk density. To do this we will first summarize the sidewalk density variable:

summary(mc_commute_long$sidewalk_density)
   Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
   0.00   18.19   22.63   24.18   35.70   59.41 

Copy the dataframe used to estimate the model, but only enough columns to explore sidewalk densities in the range between 0 and 60 , in intervals of 5. Therefore we need to copy 60 rows from the long table (thirteen levels of sidewalk density times four alternatives):

mc_commute_predict <- mc_commute_long[1:52,]

Replace the time variable using values for times 1 to 20. Since each alternative is a row, we need to create a sequence of replicated values as follows:

mc_commute_predict$sidewalk_density <- rep(seq(0, 60, 5), each = 4)

We can examine the results of simulating the sidewalk density:

select(mc_commute_predict, sidewalk_density) %>% head(8)

The prediction dataframe now includes the range of sidewalk densities that we are interested in.

The setup for the simulation essentially amounts to: what is the probability of choosing each mode for a trip that would take 1 min? For the simulation, we also need to set the values of other variables of interest. Since the model has the variable time we need to also set it to a desired value, for instance, the median trip duration (paying attention to remove the missing values):

median(mc_commute_long$time, na.rm = TRUE)
[1] 10

The median trip length is 10 minutes, so we replace the current values in the prediction dataframe:

mc_commute_predict$time <- 10

Examine the relevant variables of the prediction dataframe:

mc_commute_predict %>% select(time, sidewalk_density) %>% summary()
      time    sidewalk_density
 Min.   :10   Min.   : 0      
 1st Qu.:10   1st Qu.:15      
 Median :10   Median :30      
 Mean   :10   Mean   :30      
 3rd Qu.:10   3rd Qu.:45      
 Max.   :10   Max.   :60      

Next, predict the probabilities using the predict() function and model2:

probs <- predict(model2, newdata = mc_commute_predict)

The value (output) of predict is a 20-by-4 matrix that contains the probability for twenty travel time values (i.e., \(1, 2, 3,\cdots, 20\)), and four modes (Walk, Cycle, HSR, Car). To facilitate plotting, we add the time values and then reshape that 10-by-4 matrix as follows:

probs <- data.frame(sidewalk_density = seq(0, 60, 5), probs) %>% 
  gather(key = "Mode", value = "Probability", -sidewalk_density)

By “gathering” the probabilities, now the data frame has one column with the mode and one column with the probability. We can then plot, using a different colors for each alternative:

ggplot(data = probs, aes(x = sidewalk_density, y = Probability, color = Mode)) +
  geom_line()

We can see that the probability of walking (for a trip that takes the median duration in the sample) tends to increase as the density of sidewalks increase. The probability of using the three other modes tends to decrease with sidewalk density, but more rapidly for HSR than for car or cycling.

Comparing models: McFadden’s \(\rho^2\)

The log-likelihood reported in the summary of the model is useful as a measure of goodness of fit. Recall that the likelihood of this model is bounded between \(0\) and \(1\), and therefore the log-likelihood is bounded at the upper end by \(0\) (it is minus infinity at the lower end). We also know that higher values of the likelihood represent better fits.

One simple diagnostic to compare the fit of models is McFadden’s \(\rho^2\). This summary diagnostic is defined as follows: \[ \rho^2 = 1 - \frac{l^*}{l_0} \] where \(l^*\) is the value of the maximized log-likelihood and \(l_0\) is the value of the log-likelihood of a null model (perhaps without constants, or a constants only model). If the model is uninformative, its log-likelihood will tend to the likelihood of the null model. In this case \(l^*/l_0\) tends to one and therefore \(\rho^2\) tends to zero. If the maximized log-likelihood of the model tends to 0 (the upper limit for the log-likelihood function), \(\rho^2\) tends to one.

Although \(\rho^2\) is bounded between zero and one, just like the coefficient of determination \(R^2\) in regression analysis, its interpretation is not the same as for \(R^2\). Whereas \(R^2\) is interpreted as the proportion of variance explained by the model, \(\rho^2\) lacks such an interpretation. Also, the values of \(\rho^2\) tend to be lower, and values of \(0.4\) are conventionally considered very good fits. The main utility of McFadden’s \(\rho^2\) is as a quick way of comparing the relative fit of different models, rather than assessing the fit against an absolute value of goodness of fit.

Comparing models: the likelihood ratio test

Another way to compare models is by means of the likelihood ratio test. This test compares the log-likelihood of two models to assess whether they are significantly different. The test follows the \(\chi^2\) distribution with degrees of freedom equal to the difference in the number of coefficients between the two models. The test requires a base model and a full model, and the base model must nest within the full model. Nesting in this sense means that full model must be reducible to the base model by setting some coefficients to zero.

For example, consider the utility functions of model2: \[ \begin{array}{l} V_{i\text{Cycle}} = 0 &+& \beta_1\text{time}_{i\text{Cycle}} &+& 0\\ V_{i\text{Walk}} = \mu_{\text{Walk}} &+& \beta_1\text{time}_{i\text{Walk}} &+& \gamma_{1}\text{swd}_{i}\\ V_{i\text{HSR}} = \mu_{\text{HSR}} &+& \beta_1\text{time}_{i\text{HSR}} &+& \gamma_{2}\text{swd}_{i}\\ V_{i\text{HSR}} = \mu_{\text{Car}} &+& \beta_1\text{time}_{i\text{Car}} &+& \gamma_{3}\text{swd}_{i}\\ \end{array} \]

We can reduce this model to model1 by setting \(\gamma_{1}=\gamma_{2}=\gamma_{3}=0\): \[ \begin{array}{l} V_{i\text{Cycle}} = 0 &+& \beta_1\text{time}_{i\text{Cycle}}\\ V_{i\text{Walk}} = \mu_{\text{Walk}} &+& \beta_1\text{time}_{i\text{Walk}}\\ V_{i\text{HSR}} = \mu_{\text{HSR}} &+& \beta_1\text{time}_{i\text{HSR}}\\ V_{i\text{HSR}} = \mu_{\text{Car}} &+& \beta_1\text{time}_{i\text{Car}}\\ \end{array} \]

In this way, model1 “nests” in model2.

In the summary of the models, the likelihood ratio test is reported. See:

summary(model1)

Call:
mlogit(formula = choice ~ time, data = mc_commute_long, method = "nr")

Frequencies of alternatives:
     Car    Cycle      HSR     Walk 
0.204364 0.034182 0.244364 0.517091 

nr method
5 iterations, 0h:0m:0s 
g'(-H)^-1g = 0.000102 
successive function values within tolerance limits 

Coefficients :
                   Estimate Std. Error z-value  Pr(>|z|)    
Cycle:(intercept)  0.366898   0.201434  1.8214   0.06854 .  
HSR:(intercept)    0.707267   0.118104  5.9885 2.118e-09 ***
Walk:(intercept)   3.212834   0.184169 17.4451 < 2.2e-16 ***
time              -0.056494   0.005685 -9.9374 < 2.2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Log-Likelihood: -768.63
McFadden R^2:  0.50323 
Likelihood ratio test : chisq = 1557.2 (p.value = < 2.22e-16)

The test reported in the output of the model is against the null model, that is, a model with no variables at all. This is the least informative of all models.

When two non-null models need to be compared, the lrtest function implements the likelihood ratio test for two inputs, which are two mlogit models, as follows:

lrtest(model1, model2)
Likelihood ratio test

Model 1: choice ~ time
Model 2: choice ~ time | sidewalk_density
  #Df  LogLik Df  Chisq Pr(>Chisq)    
1   4 -768.63                         
2   7 -760.42  3 16.429  0.0009258 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Notice that the number of degrees of freedom (Df) is \(3\): this is because there are three individual-specific parameters in model2 that are not present in model1. The null hypothesis of the test is that the log-likelihood of the two models is not different, in other words, that the alternate model is not an improvement over the base model.

In the present case, the very small \(p\)-value leads us to reject the null hypothesis, and the conclusion is that model2, which includes age, is a significant improvement over model1, which does not.

Exercise

  1. In the example in this chapter we estimated the probabilities of choosing different modes by sidewalk density setting travel time to the in-sample median. Use model2 to calculate the probability of choosing each mode but now for travel times 20, 30, 40. Discuss the results.

  2. Estimate a model using formula f3 (call it model3). Discuss the output of this model.

  3. Use the likelihood ratio test to compare model3 to model1.

  4. Can you use the likelihood ratio test to compare model3 to model2? Discuss.

LS0tDQp0aXRsZTogIjA0IFByYWN0aWNhbCBNYXR0ZXJzIg0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCg0KIyBQcmFjdGljYWwgaXNzdWVzIGluIHNwZWNpZmljYXRpb24gYW5kIGVzdGltYXRpb24geyNjaGFwdGVyLTR9DQoNCj4gICJJbiB0aGVvcnksIHRoZXJlIGlzIG5vIGRpZmZlcmVuY2UgYmV0d2VlbiB0aGVvcnkgYW5kIHByYWN0aWNlLiBCdXQgaW4gcHJhY3RpY2UsIHRoZXJlIGlzLiINCj4NCj4gLS0tIEJlbmphbWluIEJyZXdzdGVyDQoNCg0KPiAiQW4gb3VuY2Ugb2YgcHJhY3RpY2UgaXMgZ2VuZXJhbGx5IHdvcnRoIG1vcmUgdGhhbiBhIHRvbiBvZiB0aGVvcnkuDQo+DQo+IC0tLSBFLkYuIFNjaHVtYWNoZXINCg0KIyMgVGhlb3J5IGFuZCBwcmFjdGljZQ0KDQpDaGFwdGVycyBcQHJlZihjaGFwdGVyLTIpIGFuZCBcQHJlZihjaGFwdGVyLTMpIHByZXNlbnRlZCBhIGNvbmNlcHR1YWwgZnJhbWV3b3JrIChhIHRoZW9yeSBvZiBiZWhhdmlvcikgYW5kIHRoZSBuZWNlc3NhcnkgYXBwYXJhdHVzIChiYXNlZCBvbiBwcm9iYWJpbGl0eSB0aGVvcnkpIHRvIGltcGxlbWVudCB0aGUgY29uY2VwdHVhbCBmcmFtZXdvcmsuIFRoaXMgdGhlb3JldGljYWwgaW50cm9kdWN0aW9uIHdhcyBuZWNlc3NhcnkgdG8gYmVnaW4gd29yayBmcm9tIGEgc29saWQgZm91bmRhdGlvbiwgYW5kIGl0IHByb3ZpZGVzIGFuIGludHVpdGl2ZSBhbmQgZWxlZ2FudCBmcmFtZXdvcmsgdG8gc3R1ZHkgZGVjaXNpb24tbWFraW5nLCBhbmQgYSBwb3dlcmZ1bCBvbmUgdG9vOyBEYW5pZWwgTWNGYWRkZW4gd2FzIGF3YXJkZWQgdGhlIFN2ZXJpZ2VzIFJpa3NiYW5rIFByaXplIGluIEVjb25vbWljIFNjaWVuY2VzIChbTm9iZWwgUHJpemVdKGh0dHBzOi8vd3d3Lm5vYmVscHJpemUub3JnL3ByaXplcy9lY29ub21pYy1zY2llbmNlcy8yMDAwL21jZmFkZGVuL2RpcGxvbWEvKSkgZm9yIGhpcyBjb250cmlidXRpb25zIHRvIHJhbmRvbSB1dGlsaXR5IG1vZGVsbGluZy4NCg0KQWx0aG91Z2ggbm90IGRlc2NyaWJlZCBpbiBkZXRhaWwgaW4gcHJldmlvdXMgY2hhcHRlcnMsIGl0IGlzIHdvcnRod2hpbGUgdG8gZHdlbGwgZm9yIGEgbW9tZW50IG9uIHRoZSBoaXN0b3J5IG9mIHRoZSBkZXZlbG9wbWVudCBvZiB0aGUgbG9naXQgbW9kZWwgYXMgYSByYW5kb20gdXRpbGl0eSBtb2RlbC4NCg0KSW4gaGlzIE5vYmVsIExlY3R1cmUsIFtNY0ZhZGRlbl0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvRGFuaWVsX01jRmFkZGVuKSBbLUBNY0ZhZGRlbjIwMDFlY29ub21pY10gcmVjb3VudHMgdGhlIHBhdGggdGhhdCBsZWQgdG8gdGhlIGRldmVsb3BtZW50IG9mIHJhbmRvbSB1dGlsaXR5IG1vZGVscyBmb3IgZGlzY3JldGUgY2hvaWNlcy4gTGlrZSBtb3N0IGltcG9ydGFudCBkaXNjb3ZlcmllcywgaXQgaXMgYSBtZWFuZGVyaW5nIHBhdGguIEl0IGJlZ2FuIGVhcmx5IGluIHRoZSAyMHRoIGNlbnR1cnkgd2l0aCBhIHRoZW9yeSBmb3IgZWNvbm9taWMgYmVoYXZpb3IgKGkuZS4sIHV0aWxpdHkpIHRoYXQgY29uc2lkZXJlZCBoZXRlcm9nZW5lb3VzIHByZWZlcmVuY2VzIHRoYXQgaW4gcHJhY3RpY2Ugd2VyZSBkaWZmaWN1bHQgdG8gdmVyaWZ5IGVtcGlyaWNhbGx5IGJlY2F1c2Ugb2YgZGF0YSBsaW1pdGF0aW9ucy4gSW5kZWVkLCBzdHVkaWVzIGJlZm9yZSB0aGUgMTk2MHMgbW9zdGx5IGNvbnNpZGVyZWQgYWdncmVnYXRlZCBkZW1hbmQgd2l0aCByZXByZXNlbnRhdGl2ZSBhZ2VudHMgKGkuZS4sIGFyY2hldHlwaWNhbCBjb25zdW1lcnMpIHRvIGFjY29tbW9kYXRlIHRoaXMgbGltaXRhdGlvbiBpbiBkYXRhIGF2YWlsYWJpbGl0eS4gSXQgd2FzIG9ubHkgd2hlbiBpbmRpdmlkdWFsLWxldmVsIGRhdGEgYmVjYW1lIG1vcmUgd2lkZWx5IGNvbGxlY3RlZCBhbmQgd2l0aGluIHJlYWNoIG9mIHJlc2VhcmNoZXJzIHRoYXQgaXQgYmVjYW1lIHBvc3NpYmxlIHRvIHBheSBhdHRlbnRpb24gdG8gdGhlIGJlaGF2aW9yIG9mIGluZGl2aWR1YWwgYWdlbnRzLiANCg0KV2hpbGUgZWNvbm9taXN0cyB3ZXJlIGJ1c3kgd2l0aCBtb2RlbHMgb2YgYWdncmVnYXRlZCBkZW1hbmQsIHJlc2VhcmNoZXJzIGluIHBzeWNob21ldHJpY3MgYW5kIG1hdGhlbWF0aWNhbCBwc3ljaG9sb2d5LCBjaGllZmx5IFtMLkwgVGh1cnN0b25lXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9Mb3Vpc19MZW9uX1RodXJzdG9uZSkgYW5kIFtSLkQuIEx1Y2VdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL1IuX0R1bmNhbl9MdWNlKSwgd2VyZSBidXN5IHByb3ZpZGluZyB0aGUgdGVjaG5pY2FsIGJhc2lzIGZvciBtb2RlbGxpbmcgd2hhdCBUaHVyc3RvbmUgdGVybWVkIF9Db21wYXJhdGl2ZSBKdWRnZW1lbnRfIChpbiB0aGUgc2Vuc2Ugb2YgbWFraW5nIGEgZGVjaXNpb24gb3IgZm9ybWluZyBhbiBvcGluaW9uKS4gSW4gcGFydGljdWxhciwgTHVjZSBpbnRyb2R1Y2VkIHRoZSBheGlvbSBvZiBJbmRlcGVuZGVuY2Ugb2YgSXJyZWxldmFudCBBbHRlcm5hdGl2ZXMgKGRpc2N1c3NlZCBpbiBDaGFwdGVyIFxAcmVmKGNoYXB0ZXItMykpLiBBY2NvcmRpbmcgdG8gTWNGYWRkZW4gKDIwMDEsIHAuIDM1MyksIHRoaXMgYXhpb20gInNpbXBsaWZpZWQgZXhwZXJpbWVudGFsIGNvbGxlY3Rpb24gb2YgY2hvaWNlIGRhdGEgYnkgYWxsb3dpbmcgbXVsdGlub21pYWwgY2hvaWNlIHByb2JhYmlsaXRpZXMgdG8gYmUgaW5mZXJyZWQgZnJvbSBiaW5vbWlhbCBjaG9pY2UgZXhwZXJpbWVudHMuIiBbSi4gTWFyc2NoYWtdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0phY29iX01hcnNjaGFrKSB3YXMgdGhlIGZpcnN0IHRvIGludHJvZHVjZSB0aGUgd29yayBvZiBUaHVyc3RvbmUgdG8gZWNvbm9tZXRyaWNzIGluIDE5NjAsIGFuZCBhbHNvIGNvaW5lZCB0aGUgdGVybSBfUmFuZG9tIFV0aWxpdHkgTWF4aW1pemluZ18gKFJVTSkgdGhhdCBldmVudHVhbGx5IHByZXZhaWxlZCBvdmVyIHRoZSBjb21wYXJhdGl2ZSBqdWRnZW1lbnQgdGVybWlub2xvZ3kgb2YgVGh1cnN0b25lLiBNY0ZhZGRlbidzIGVhcmx5IGNvbnRyaWJ1dGlvbnMgdG8gdGhpcyBib2R5IG9mIHJlc2VhcmNoIHdhcyBkZXZlbG9waW5nIGFuIGVjb25vbWV0cmljIHZlcnNpb24gb2YgTHVjZSdzIG1vZGVsLCB3aXRoIHN0cmljdCAoaS5lLiwgc3lzdGVtYXRpYykgdXRpbGl0aWVzIHNwZWNpZmllZCBhcyBmdW5jdGlvbnMgb2YgdGhlIGF0dHJpYnV0ZXMgb2YgdGhlIGFsdGVybmF0aXZlcy4gVGhpcyBhbGxvd2VkIGEgcmVzZWFyY2ggdG8gbGluayB1bm9ic2VydmVkIHByZWZlcmVuY2UgaGV0ZXJvZ2VuZWl0eSB0byBhIGZ1bGx5IGNvbnNpc3RlbnQgZGVzY3JpcHRpb24gb2YgdGhlIGRpc3RyaWJ1dGlvbiBvZiBkZW1hbmQuIFNpbmNlIHRoZSAxOTcwcywgZGlzY3JldGUgY2hvaWNlIGFuYWx5c2lzIGhhcyBiZWVuIGEgYnVyZ2VvbmluZyBhcmVhIG9mIHJlc2VhcmNoIHdpdGggYSBwbGV0aG9yYSBvZiBhcHBsaWNhdGlvbnMgaW4gZWNvbm9taWNzLCBtYXJrZXRpbmcsIGFuZCB0cmF2ZWwgYmVoYXZpb3IsIGFtb25nIG1hbnkgb3RoZXIgZGlzY2lwbGluZXMuDQoNClRoaXMgYnJpZWYgc3RvcnkgbmVhdGx5IGlsbHVzdHJhdGVzIHRoZSBjb21wbGV4IGludGVycGxheSBiZXR3ZWVuIHRoZW9yeSBhbmQgcHJhY3RpY2UuDQoNCkVhcmx5IGF0dGVtcHRzIHRvIHN0dWR5IGRlbWFuZCB3ZXJlIGxpbWl0ZWQgZHVlIHRvIHByYWN0aWNhbCBjb25zaWRlcmF0aW9ucyAoaS5lLiwgdGhlIGFic2VuY2Ugb2YgZGF0YSBhdCB0aGUgaW5kaXZpZHVhbCBsZXZlbCkuIE9uY2UgYXBwcm9wcmlhdGUgZGF0YSBiZWNhbWUgYXZhaWxhYmxlLCBuZXcgc3R1ZGllcyBjb250aW51ZWQgdG8gcHVzaCB0aGUgdGhlb3JldGljYWwgZW52ZWxvcGUuIEluZGVlZCwgdGhlb3JldGljYWwgcXVlc3Rpb25zIGhhdmUgY29udGludWVkIHRvIGluc3BpcmUgbmV3ZXIgd2F5IHRvIGNvbGxlY3QgZGF0YSBhbmQgbm92ZWwgbWV0aG9kcywgYW5kIHRoZXNlIGluIHR1cm4gaGF2ZSBoZWxwZWQgdXMgdG8gcmVmaW5lIG91ciB1bmRlcnN0YW5kaW5nIG9mIGJlaGF2aW9yLiBTZWUgYXMgYW4gZXhhbXBsZSB0aGUgd29yayBvbiBkZWNpc2lvbi1tYWtpbmcgaW4gc29jaWFsIHNpdHVhdGlvbnMgW0BBa2VybG9mMTk5N3NvY2lhbDsgQEF4aGF1c2VuMjAwNXNvY2lhbDsgQFBhZXoyMDA3c29jaWFsXSB3aGljaCBpbnNwaXJlZCB0aGUgdXNlIG9mIG5ldyBkYXRhIHNvdXJjZXMgW2UuZy4sIEBDYXJyYXNjbzIwMDdzb2NpYWwgQEF4aGF1c2VuMjAwOHNvY2lhbDsgQFNjb3R0MjAxMnNvY2lhbDsgQENoZW4yMDE2c29jaWFsXSBhcyB3ZWxsIGFzIG5vdmVsIG1vZGVsbGluZyBhcHByb2FjaGVzIFtlLmcuLCBARHVndW5kamkyMDA1ZGlzY3JldGU7IEBEdWd1bmRqaTIwMTNzb2NpYWw7IEBLYW1hcmdpYW5uaTIwMTRzb2NpYWxdIGFuZCBlbXBpcmljYWwgd29yayBbZS5nLiwgQHZhbmRlbkJlcmcyMDA5c29jaWFsOyBAR29ldHprZTIwMTFiaWN5Y2xlOyBATWF0b3VzMjAxN3NvY2lhbF0uDQoNCk5vdyB0aGF0IHRoZSBwcmVjZWRpbmcgY2hhcHRlcnMgaGF2ZSBhcm1lZCB1cyB3aXRoIHRoZSB0aGVvcnkgYW5kIGJhc2ljIGNvbmNlcHRzIHRvIGltcGxlbWVudCByYW5kb20gdXRpbGl0eSBtb2RlbHMsIGl0IGlzIHByb3BlciB0aGF0IHdlIHR1cm4gb3VyIGF0dGVudGlvbiB0byB0aGUgcHJhY3RpY2FsIGFzcGVjdHMgb2YgbW9kZWxsaW5nLiBUaGUgYmVzdCB3YXkgdG8gZW5zdXJlIHRoYXQgdGhlIGNvbmNlcHRzIHRha2UgaG9sZCwgaW4gbXkgdmlldywgaXMgdG8gZ2V0IHlvdXIgaGFuZHMgb24gYSBkYXRhc2V0IGFuZCBzdHJ1Z2dsZSB3aXRoIHRoZSBwcmFjdGljYWxpdGllcyBvZiBjbGVhbmluZyBhbmQgb3JnYW5pemluZyBkYXRhLCBzcGVjaWZ5aW5nIHRoZSB1dGlsaXR5IGZ1bmN0aW9ucyAoYSB0YXNrIHRoYXQgaXMgbW9yZSBhcnQgdGhhbiBzY2llbmNlKSwgYW5kIGVzdGltYXRpbmcgbW9kZWxzLiBUaGVzZSBza2lsbHMgYXJlIG1vc3RseSB0cmFuc2ZlcmFibGUgdG8gb3RoZXIgbW9kZWxsaW5nIHRlY2huaXF1ZXMsIHNvIHdlIHdpbGwgYmVnaW4gYnkgYXBwbHlpbmcgdGhlbSB0byB0aGUgbW9zdCBmdW5kYW1lbnRhbCBvZiBkaXNjcmV0ZSBjaG9pY2UgbW9kZWxzLCBuYW1lbHkgdGhlIG11bHRpbm9taWFsIGxvZ2l0LiAgDQoNCiMjIEhvdyB0byB1c2UgdGhpcyBub3RlDQoNClJlbWVtYmVyIHRoYXQgdGhlIHNvdXJjZSBmb3IgdGhlIGRvY3VtZW50IHlvdSBhcmUgcmVhZGluZyBpcyBhbiBSIE5vdGVib29rLiBUaHJvdWdob3V0IHRoZSBub3RlcywgeW91IHdpbGwgZmluZCBleGFtcGxlcyBvZiBjb2RlIGluIHNlZ21lbnRzIG9mIHRleHQgY2FsbGVkIF9jaHVua3NfLiBUaGlzIGlzIGFuIGV4YW1wbGUgb2YgYSBjaHVuazoNCmBgYHtyfQ0KcHJpbnQoIkhhdHMgb2ZmIHRvIHlvdSwgUHJvZi4gTWNGYWRkZW4iKQ0KYGBgDQoNCklmIHlvdSBhcmUgd29ya2luZyB3aXRoIHRoZSBOb3RlYm9vayB2ZXJzaW9uIG9mIHRoZSBkb2N1bWVudCwgeW91IGNhbiBydW4gdGhlIGNvZGUgYnkgY2xpY2tpbmcgdGhlICdwbGF5JyBpY29uIG9uIHRoZSB0b3AgcmlnaHQgY29ybmVyIG9mIHRoZSBjaHVuay4gSWYgeW91IGFyZSByZWFkaW5nIHRoZSB3ZWItYm9vayB2ZXJzaW9uIG9mIHRoZSBkb2N1bWVudCwgeW91IHdpbGwgb2Z0ZW4gc2VlIHRoYXQgdGhlIGNvZGUgaGFzIGFscmVhZHkgYmVlbiBleGVjdXRlZC4gWW91IGNhbiBzdGlsbCB0cnkgaXQgYnkgY29weWluZyBhbmQgcGFzdGluZyBpbnRvIHlvdXIgUiBvciBSU3R1ZGlvIGNvbnNvbGUuDQoNCiMjIExlYXJuaW5nIG9iamVjdGl2ZXMNCg0KSW4gdGhpcyBwcmFjdGljZSwgeW91IHdpbGwgbGVhcm4gYWJvdXQ6DQoNCjEuIFNwZWNpZmljYXRpb24gb2YgdXRpbGl0eSBmdW5jdGlvbnMuDQoyLiBNYXhpbXVtIGxpa2VsaWhvb2QgZXN0aW1hdGlvbi4NCjMuIEVzdGltYXRpb24gb2YgbXVsdGlub21pYWwgbG9naXQgbW9kZWxzLg0KNC4gTWNGYWRkZW4ncyAkXHJob14yJA0KNS4gVGhlIGxpa2VsaWhvb2QgcmF0aW8gdGVzdC4NCg0KIyMgU3VnZ2VzdGVkIHJlYWRpbmdzDQoNCi0gQmVuLUFraXZhLCBNLiBMZXJtYW4sIFstQEJlbmFraXZhMTk4NWRpc2NyZXRlXSBEaXNjcmV0ZSBDaG9pY2UgQW5hbHlzaXM6IFRoZW9yeSBhbmQgQXBwbGljYXRpb25zIHRvIFRyYXZlbCBEZW1hbmQsICoqQ2hhcHRlcnMgNCBhbmQgNSoqLCBNSVQgUHJlc3MuDQotIEhlbnNoZXIsIEQuQS4sIFJvc2UsIEouTS4sIEdyZWVuZSwgVy5IIFstQGhlbnNoZXIyMDA1YXBwbGllZF0gQXBwbGllZCBDaG9pY2UgQW5hbHlzaXM6IEEgUHJpbWVyLCAqKkNoYXB0ZXIgMTAqKiwgQ2FtYnJpZGdlIFVuaXZlcnNpdHkgUHJlc3MuDQotIE9ydHV6YXIgSkQsIFdpbGx1bXNlbiBMRyBbLUBPcnR1emFyMjAxMW1vZGVsbGluZ10gTW9kZWxsaW5nIFRyYW5zcG9ydCwgRm91cnRoIEVkaXRpb24sICoqQ2hhcHRlciA4KiosIEpvaG4gV2lsZXkgYW5kIFNvbnMuDQotIFRyYWluIFstQFRyYWluMjAwOWRpc2NyZXRlXSBEaXNjcmV0ZSBDaG9pY2UgTWV0aG9kcyB3aXRoIFNpbXVsYXRpb24sIFNlY29uZCBFZGl0aW9uLCAqKkNoYXB0ZXIgMyoqLCBDYW1icmlkZ2UgVW5pdmVyc2l0eSBQcmVzcy4NCg0KIyMgUHJlbGltaW5hcmllcw0KDQpMb2FkIHRoZSBwYWNrYWdlcyB1c2VkIGluIHRoaXMgc2VjdGlvbjoNCmBgYHtyIG1lc3NhZ2U9RkFMU0V9DQpsaWJyYXJ5KGthYmxlRXh0cmEpDQpsaWJyYXJ5KG1sb2dpdCkNCmxpYnJhcnkocGxvdGx5KQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpgYGANCg0KTG9hZCB0aGUgZGF0YXNldCB1c2VkIGluIHRoaXMgc2VjdGlvbjoNCmBgYHtyfQ0KbG9hZCgiQ29tbXV0ZSBNYWMgV2lkZS5SRGF0YSIpDQpgYGANCg0KIyMgVGhlIGFuYXRvbXkgb2YgdXRpbGl0eSBmdW5jdGlvbnMNCg0KQXQgdGhlIGVuZCBDaGFwdGVyIFxAcmVmKGNoYXB0ZXItMykgd2UgdG9vaywgZm9yIHRoZSBmaXJzdCB0aW1lLCBhIGNsb3NlciBsb29rIGF0IHRoZSBzeXN0ZW1hdGljIHV0aWxpdGllcyBvZiBkaXNjcmV0ZSBjaG9pY2UgbW9kZWxzLiBJdCBpcyB1c2VmdWwgdG8gdGhpbmsgYWJvdXQgdGhlIGFuYXRvbXkgb2YgYSB0eXBpY2FsIHN5c3RlbWF0aWMgdXRpbGl0eSBmdW5jdGlvbi4gUHJldmlvdXNseSwgd2Ugc2FpZCB0aGF0IHNvbWUgdmFyaWFibGVzIHZhcnkgYWNyb3NzIHV0aWxpdHkgZnVuY3Rpb25zOyB0aGVzZSBhcmUgdHlwaWNhbGx5IHRoZSBhdHRyaWJ1dGVzIHRoYXQgZGVzY3JpYmUgdGhlIHZhcmlvdXMgYWx0ZXJuYXRpdmVzIChlLmcuLCB0aGVpciBsZXZlbCBvZiBzZXJ2aWNlIGFuZCBjb3N0KS4gVGhlIHZhcmlhYmxlcyB0aGF0IGRlc2NyaWJlIHRoZSBkZWNpc2lvbi1tYWtlciBkbyBfbm90XyB2YXJ5IGJ5IGFsdGVybmF0aXZlLiBUaGlzIGhhcyBpbXBsaWNhdGlvbnMsIGFzIHNlZW4gYmVmb3JlLCBmb3IgaG93IHRoZSB2YXJpYWJsZXMgYXJlIGVudGVyZWQgaW50byB0aGUgZnVuY3Rpb25zLiBTaW5jZSB0aGUgbW9kZWwgd29ya3Mgb24gdGhlIGJhc2lzIG9mIF9kaWZmZXJlbmNlc18gYmV0d2VlbiB1dGlsaXRpZXMsIHRoZSBhdHRyaWJ1dGVzIG11c3QgYWN0dWFsbHkgbWVhc3VyZSBkaWZmZXJlbnQgbGV2ZWxzIG9mIHNvbWV0aGluZyBvciB2YW5pc2guDQoNCldlIHdpbGwgZGVzY3JpYmUgdGhlIHV0aWxpdGllcyBpbiB0ZXJtcyBvZiB0aGUgd2F5IGRpZmZlcmVudCB2YXJpYWJsZXMgYXJlIGludHJvZHVjZWQgaW4gdGhlIHV0aWxpdHkgZnVuY3Rpb25zLiBBcyBiZWZvcmUsIHdlIHdpbGwgYXNzdW1lIHRoYXQgdGhlIGxvY2F0aW9uIHBhcmFtZXRlcnMgb2YgdGhlIGRpc3RyaWJ1dGlvbiBhcmUgYWJzb3JiZWQgYnkgJEotMSQgdXRpbGl0eSBmdW5jdGlvbnMgKHdoZXJlICRKJCBpcyB0aGUgbnVtYmVyIG9mIGFsdGVybmF0aXZlcykgaW4gdGhlIGZvcm0gb2YgYWx0ZXJuYXRpdmUgc3BlY2lmaWMgY29uc3RhbnRzLiANCg0KQ29uc2lkZXIgZmlyc3QgdmFyaWFibGVzIHRoYXQgdmFyeSBhY3Jvc3MgYWx0ZXJuYXRpdmVzLiBUaGVzZSB2YXJpYWJsZXMgY2FuIGhhdmUgYSBnZW5lcmljIGNvZWZmaWNpZW50IG9yIHRoZXkgY2FuIGhhdmUgYWx0ZXJuYXRpdmUtc3BlY2lmaWMgY29lZmZpY2llbnRzLCBhcyBzZWVuIGhlcmU6DQoNCiQkDQpcYmVnaW57YXJyYXl9e2x9DQogICAgICAgICAgICAgICAgVl97aTF9ID1cXA0KICAgICAgICAgICAgICAgIFZfe2kyfSA9XFwNCiAgICAgICAgICAgICAgICBWX3tpM30gPVxcDQogICAgICAgICAgICAgIFxlbmR7YXJyYXl9ICANCiAgXG92ZXJicmFjZXsgXGJlZ2lue2FycmF5fXtsbGx9DQogICAgICAgICAgICAgICAgMCAmICswICYgKzBcXA0KICAgICAgICAgICAgICAgIDAgJiArXG11XzIgJiArMCBcXA0KICAgICAgICAgICAgICAgIDAgJiArMCAmICtcbXVfM1xcDQogICAgICAgICAgICAgIFxlbmR7YXJyYXl9DQogICAgICAgICAgICAgIH1eXHRleHR7YWx0ZXJuYXRpdmUgc3BlY2lmaWMgY29uc3RhbnRzfSANCiAgXHVuZGVyYnJhY2V7XGJlZ2lue2FycmF5fXtsbGx9DQogICAgICAgICAgICAgICAgK1xiZXRhXzF4X3tpMX1cXA0KICAgICAgICAgICAgICAgICtcYmV0YV8xeF97aTJ9XFwNCiAgICAgICAgICAgICAgICArXGJldGFfMXhfe2kzfVxcDQogICAgICAgICAgICAgIFxlbmR7YXJyYXl9DQogICAgICAgICAgICAgIH1fe1x0ZXh0e2FsdGVybmF0aXZlIHZhcnMuIHdpdGggZ2VuZXJpYyBjb2VmZmljaWVudHN9fQ0KICBcb3ZlcmJyYWNle1xiZWdpbnthcnJheX17bGxsfQ0KICAgICAgICAgICAgICAgICArXGRlbHRhXzF3X3tpMX0gJiArMCAmICswXFwNCiAgICAgICAgICAgICAgICArMCAmICtcZGVsdGFfMndfe2kyfSAmICswXFwNCiAgICAgICAgICAgICAgICArMCAmICswICYgK1xkZWx0YV8zd197aTN9XFwNCiAgICAgICAgICAgICAgXGVuZHthcnJheX0NCiAgICAgICAgICAgICAgfV57XHRleHR7YWx0ZXJuYXRpdmUgdmFycy4gd2l0aCBzcGVjaWZpYyBjb2VmZmljaWVudHN9fQ0KJCQNCg0KSW4gbWFueSBjYXNlcyBpdCBpcyBzZW5zaWJsZSB0byBoYXZlIGdlbmVyaWMgY29lZmZpY2llbnRzLiBGb3IgaW5zdGFuY2UsIGlmIHRoZSB2YXJpYWJsZSBpcyBjb3N0LCB3ZSBtaWdodCBhc3N1bWUgdGhhdCBvbmUgZG9sbGFyIGlzIHZhbHVlZCBlcXVhbGx5IGlycmVzcGVjdGl2ZSBvZiB0aGUgd2hpY2ggYWx0ZXJuYXRpdmUgaXQgaXMgc3BlbnQgb24uIEluIG90aGVyIGNhc2VzLCB0aGUgdXNlIG9mIGFsdGVybmF0aXZlLXNwZWNpZmljIGNvZWZmaWNpZW50cyBtaWdodCBiZSBpbmZvcm1hdGl2ZS4gRm9yIGluc3RhbmNlLCBhIGNvbnNpc3RlbnQgZmluZGluZyBpbiB0aGUgbGl0ZXJhdHVyZSBpcyB0aGF0IHRpbWUgd2FpdGluZyBmb3IgYSBidXMgaXMgcGVyY2VpdmVkIGFzIGJlaW5nIG1vcmUgZXhwZW5zaXZlIHRoYW4gdGltZSBhY3R1YWxseSBvbi1ib2FyZCBhbmQgdHJhdmVsaW5nIGluIHRoZSBidXMuIE9jY2FzaW9uYWxseSwgYXMgd2VsbCwgYW4gYXR0cmlidXRlIG1pZ2h0IGJlIHNwZWNpZmljIHRvIGFuIGFsdGVybmF0aXZlOiBmb3IgaW5zdGFuY2UsIHdhaXRpbmcgdGltZSBpcyBvZnRlbiBpbXBsaWNpdGx5IHplcm8gZm9yIHRyYXZlbCBieSBjYXIgYW5kIGFjdGl2ZSBtb2RlcyBvZiB0cmFuc3BvcnRhdGlvbiAoaS5lLiwgd2Fsa2luZyBhbmQgY3ljbGluZykuDQoNClRoZSBkaWZmZXJlbmNlcyBvZiB0aGUgdXRpbGl0aWVzIGFyZSBhcyBmb2xsb3dzOg0KDQokJA0KXGJlZ2lue2FycmF5fXtsbGx9DQogICAgVl97aTJ9LVZfe2kxfT0mKFxtdV8yIC0gMCkgJiArICZcYmV0YV8xKHhfe2kyfSAtIHhfe2kxfSkgJiArICYoXGRlbHRhXzJ3X3tpMn0gLSBcZGVsdGFfMXdfe2kxfSlcXA0KICAgIFZfe2kzfS1WX3tpMX09JihcbXVfMyAtIDApICYgKyAmXGJldGFfMSh4X3tpMn0gLSB4X3tpMX0pICYgKyAmKFxkZWx0YV8zd197aTN9IC0gIFxkZWx0YV8xd197aTF9KVxcDQogICAgVl97aTN9LVZfe2kyfT0mKFxtdV8zLSBcbXVfMikgJiArICZcYmV0YV8xKHhfe2kyfSAtIHhfe2kxfSkgJiArICYoXGRlbHRhXzN3X3tpM30gLSBcZGVsdGFfMndfe2kyfSlcXA0KXGVuZHthcnJheX0NCiQkDQoNClZhcmlhYmxlcyB0aGF0IHZhcnkgYWNyb3NzIGluZGl2aWR1YWxzIGJ1dCBub3QgYnkgYWx0ZXJuYXRpdmUgY2FuIGJlIGludHJvZHVjZWQgd2l0aCBhbHRlcm5hdGl2ZS1zcGVjaWZpYyBjb2VmZmljaWVudHM6DQoNCiQkDQpcYmVnaW57YXJyYXl9e2x9DQogICAgICAgICAgICAgICAgVl97aTF9ID1cXA0KICAgICAgICAgICAgICAgIFZfe2kyfSA9XFwNCiAgICAgICAgICAgICAgICBWX3tpM30gPVxcDQogICAgICAgICAgICAgIFxlbmR7YXJyYXl9ICANCiAgXG92ZXJicmFjZXsgXGJlZ2lue2FycmF5fXtsbGx9DQogICAgICAgICAgICAgICAgMCAmICswICYgKzBcXA0KICAgICAgICAgICAgICAgIDAgJiArXG11XzIgJiArMCBcXA0KICAgICAgICAgICAgICAgIDAgJiArMCAmICtcbXVfM1xcDQogICAgICAgICAgICAgIFxlbmR7YXJyYXl9DQogICAgICAgICAgICAgIH1eXHRleHR7YWx0ZXJuYXRpdmUgc3BlY2lmaWMgY29uc3RhbnRzfSANCiAgXHVuZGVyYnJhY2V7XGJlZ2lue2FycmF5fXtsbGx9DQogICAgICAgICAgICAgICAgK1xiZXRhXzF4X3tpMX1cXA0KICAgICAgICAgICAgICAgICtcYmV0YV8xeF97aTJ9XFwNCiAgICAgICAgICAgICAgICArXGJldGFfMXhfe2kzfVxcDQogICAgICAgICAgICAgIFxlbmR7YXJyYXl9DQogICAgICAgICAgICAgIH1fe1x0ZXh0e2FsdGVybmF0aXZlIHZhcnMuIHdpdGggZ2VuZXJpYyBjb2VmZmljaWVudHN9fQ0KDQogIFxvdmVyYnJhY2V7XGJlZ2lue2FycmF5fXtsbGx9DQogICAgICAgICAgICAgICAgICtcZGVsdGFfMXdfe2kxfSAmICswICYgKzBcXA0KICAgICAgICAgICAgICAgICswICYgK1xkZWx0YV8yd197aTJ9ICYgKzBcXA0KICAgICAgICAgICAgICAgICswICYgKzAgJiArXGRlbHRhXzN3X3tpM31cXA0KICAgICAgICAgICAgICBcZW5ke2FycmF5fQ0KICAgICAgICAgICAgICB9XntcdGV4dHthbHRlcm5hdGl2ZSB2YXJzLiB3aXRoIHNwZWNpZmljIGNvZWZmaWNpZW50c319DQogIFx1bmRlcmJyYWNle1xiZWdpbnthcnJheX17bGxsfQ0KICAgICAgICAgICAgICAgICswICYgKzAgJlxcDQogICAgICAgICAgICAgICAgK1xnYW1tYV8yel97aX0gJiArMFxcDQogICAgICAgICAgICAgICAgKzAgJiArXGdhbW1hXzN6X3tpfVxcDQogICAgICAgICAgICAgIFxlbmR7YXJyYXl9DQogICAgICAgICAgICAgIH1fXHRleHR7aW5kaXZpZHVhbCB2YXJzIHdpdGggc3BlY2lmaWMgY29lZmZpY2llbnRzfQ0KJCQNCg0KRm9sbG93aW5nIHRoZSBleGFtcGxlIGFib3ZlLCB0aGUgZGlmZmVyZW5jZXMgb2YgdXRpbGl0aWVzIGFyZToNCg0KJCQNClxiZWdpbnthcnJheX17bGxsfQ0KICAgIFZfe2kyfS1WX3tpMX09JihcbXVfMiAtIDApICYgKyAmXGJldGFfMSh4X3tpMn0gLSB4X3tpMX0pICYgKyAmKFxkZWx0YV8yd197aTJ9IC0gXGRlbHRhXzF3X3tpMX0pICYgKyAmKFxnYW1tYV8yIC0gMCl6X2lcXA0KICAgIFZfe2kzfS1WX3tpMX09JihcbXVfMyAtIDApICYgKyAmXGJldGFfMSh4X3tpMn0gLSB4X3tpMX0pICYgKyAmKFxkZWx0YV8zd197aTN9IC0gIFxkZWx0YV8xd197aTF9KSAmICsgJihcZ2FtbWFfMyAtIDApel9pXFwNCiAgICBWX3tpM30tVl97aTJ9PSYoXG11XzMtIFxtdV8yKSAmICsgJlxiZXRhXzEoeF97aTJ9IC0geF97aTF9KSAmICsgJihcZGVsdGFfM3dfe2kzfSAtIFxkZWx0YV8yd197aTJ9KSAmICsgJihcZ2FtbWFfMyAtIFxnYW1tYV8yKXpfaVxcDQpcZW5ke2FycmF5fQ0KJCQNCg0KQSBkaWZmZXJlbnQgd2F5IG9mIGludHJvZHVjaW5nIGluZGl2aWR1YWwtbGV2ZWwgdmFyaWFibGVzIGlzIGFzIHBhcnQgb2YgYW4gZXhwYW5zaW9uIG9mIHNvbWUgY29lZmZpY2llbnRzLCBmb3IgZXhhbXBsZToNCg0KJCQNCg0KXGJlZ2lue2FycmF5fXtsfQ0KICAgICAgICAgICAgICAgIFZfe2kxfSA9XFwNCiAgICAgICAgICAgICAgICBWX3tpMn0gPVxcDQogICAgICAgICAgICAgICAgVl97aTN9ID1cXA0KICAgICAgICAgICAgICBcZW5ke2FycmF5fSAgDQogIFxvdmVyYnJhY2V7IFxiZWdpbnthcnJheX17bGxsfQ0KICAgICAgICAgICAgICAgIDAgJiArMCAmICswXFwNCiAgICAgICAgICAgICAgICAwICYgK1xtdV8yICYgKzAgXFwNCiAgICAgICAgICAgICAgICAwICYgKzAgJiArXG11XzNcXA0KICAgICAgICAgICAgICBcZW5ke2FycmF5fQ0KICAgICAgICAgICAgICB9Xlx0ZXh0e2FsdGVybmF0aXZlIHNwZWNpZmljIGNvbnN0YW50c30gDQogIFx1bmRlcmJyYWNle1xiZWdpbnthcnJheX17bGxsfQ0KICAgICAgICAgICAgICAgICsoXGJldGFfezExfSArIFxiZXRhX3sxMn16X2kpeF97aTF9XFwNCiAgICAgICAgICAgICAgICArKFxiZXRhX3sxMX0gKyBcYmV0YV97MTJ9el9pKXhfe2kyfVxcDQogICAgICAgICAgICAgICAgKyhcYmV0YV97MTF9ICsgXGJldGFfezEyfXpfaSl4X3tpM31cXA0KICAgICAgICAgICAgICBcZW5ke2FycmF5fQ0KICAgICAgICAgICAgICB9X3tcdGV4dHthbHRlcm5hdGl2ZSB2YXJzLiB3aXRoIGdlbmVyaWMgY29lZmZpY2llbnRzfX0NCiAgXG92ZXJicmFjZXtcYmVnaW57YXJyYXl9e2xsbH0NCiAgICAgICAgICAgICAgICAgK1xkZWx0YV8xd197aTF9ICYgKzAgJiArMFxcDQogICAgICAgICAgICAgICAgKzAgJiArXGRlbHRhXzJ3X3tpMn0gJiArMFxcDQogICAgICAgICAgICAgICAgKzAgJiArMCAmICtcZGVsdGFfM3dfe2kzfVxcDQogICAgICAgICAgICAgIFxlbmR7YXJyYXl9DQogICAgICAgICAgICAgIH1ee1x0ZXh0e2FsdGVybmF0aXZlIHZhcnMuIHdpdGggc3BlY2lmaWMgY29lZmZpY2llbnRzfX0NCiQkDQoNClRoZSBhYm92ZSBleHBhbmRzIHRvOg0KDQokJA0KDQpcYmVnaW57YXJyYXl9e2x9DQogICAgICAgICAgICAgICAgVl97aTF9ID1cXA0KICAgICAgICAgICAgICAgIFZfe2kyfSA9XFwNCiAgICAgICAgICAgICAgICBWX3tpM30gPVxcDQogICAgICAgICAgICAgIFxlbmR7YXJyYXl9ICANCiAgXG92ZXJicmFjZXsgXGJlZ2lue2FycmF5fXtsbGx9DQogICAgICAgICAgICAgICAgMCAmICswICYgKzBcXA0KICAgICAgICAgICAgICAgIDAgJiArXG11XzIgJiArMCBcXA0KICAgICAgICAgICAgICAgIDAgJiArMCAmICtcbXVfM1xcDQogICAgICAgICAgICAgIFxlbmR7YXJyYXl9DQogICAgICAgICAgICAgIH1eXHRleHR7YWx0ZXJuYXRpdmUgc3BlY2lmaWMgY29uc3RhbnRzfSANCiAgXHVuZGVyYnJhY2V7XGJlZ2lue2FycmF5fXtsbGx9DQogICAgICAgICAgICAgICAgK1xiZXRhX3sxMX14X3tpMX0gJiArXGJldGFfezEyfXpfaXhfe2kxfVxcDQogICAgICAgICAgICAgICAgK1xiZXRhX3sxMX14X3tpMn0gJiArIFxiZXRhX3sxMn16X2l4X3tpMn1cXA0KICAgICAgICAgICAgICAgICtcYmV0YV97MTF9eF97aTN9ICYgKyBcYmV0YV97MTJ9el9peF97aTN9XFwNCiAgICAgICAgICAgICAgXGVuZHthcnJheX0NCiAgICAgICAgICAgICAgfV97XHRleHR7YWx0ZXJuYXRpdmUgdmFycy4gd2l0aCBnZW5lcmljIGNvZWZmaWNpZW50c319DQogIFxvdmVyYnJhY2V7XGJlZ2lue2FycmF5fXtsbGx9DQogICAgICAgICAgICAgICAgK1xkZWx0YV8xd197aTF9ICYgKzAgJiArMFxcDQogICAgICAgICAgICAgICAgKzAgJiArXGRlbHRhXzJ3X3tpMn0gJiArMFxcDQogICAgICAgICAgICAgICAgKzAgJiArMCAmICtcZGVsdGFfM3dfe2kzfVxcDQogICAgICAgICAgICAgIFxlbmR7YXJyYXl9DQogICAgICAgICAgICAgIH1ee1x0ZXh0e2FsdGVybmF0aXZlIHZhcnMuIHdpdGggc3BlY2lmaWMgY29lZmZpY2llbnRzfX0NCiQkDQoNCkFuZCBzbyB0aGUgZGlmZmVyZW5jZXMgaW4gdXRpbGl0aWVzIGFyZToNCg0KJCQNClxiZWdpbnthcnJheX17bGxsfQ0KICAgIFZfe2kyfS1WX3tpMX09JihcbXVfMiAtIDApICYgKyAmXGJldGFfezExfSh4X3tpMn0gLSB4X3tpMX0pICYgKyAmXGJldGFfezExfSh6X2l4X3tpMn0gLSB6X2l4X3tpMX0pICYgKyAmKFxkZWx0YV8yd197aTJ9IC0gXGRlbHRhXzF3X3tpMX0pXFwNCiAgICBWX3tpM30tVl97aTF9PSYoXG11XzMgLSAwKSAmICsgJlxiZXRhX3sxMX0oeF97aTJ9IC0geF97aTF9KSAmICsgJlxiZXRhX3sxMX0oel9peF97aTN9IC0gel9peF97aTF9KSAmICsgJihcZGVsdGFfM3dfe2kzfSAtICBcZGVsdGFfMXdfe2kxfSlcXA0KICAgIFZfe2kzfS1WX3tpMn09JihcbXVfMy0gXG11XzIpICYgKyAmXGJldGFfezExfSh4X3tpMn0gLSB4X3tpMX0pICYgKyAmXGJldGFfezExfSh6X2l4X3tpM30gLSB6X2l4X3tpMn0pICYgKyAmKFxkZWx0YV8zd197aTN9IC0gXGRlbHRhXzJ3X3tpMn0pXFwNClxlbmR7YXJyYXl9DQokJA0KDQpVbmRlcnN0YW5kaW5nIHRoZSBhbmF0b215IG9mIHV0aWxpdHkgZnVuY3Rpb25zIGlzIGVzc2VudGlhbCB0byBwcm9wZXJseSBzcGVjaWZ5IGFuZCBlc3RpbWF0ZSBtb2RlbHMuDQoNCiMjIEV4YW1wbGU6IFNwZWNpZnlpbmcgdGhlIHV0aWxpdHkgZnVuY3Rpb25zDQoNCldlIHdpbGwgbm93IHByb2NlZWQgdG8gd29yayB3aXRoIGEgcHJhY3RpY2FsIGV4YW1wbGUsIHVzaW5nIHRoZSBkYXRhc2V0IHRoYXQgeW91IGVuY291bnRlcmVkIGJlZm9yZSBpbiBDaGFwdGVyIFxAcmVmKGNoYXB0ZXItMSkuIFRoaXMgZGF0YXNldCBjb250YWlucyBpbmZvcm1hdGlvbiBvbiB2YXJpb3VzIG1vZGVzIG9mIHRyYW5zcG9ydGF0aW9uIHVzZWQgYnkgcGVvcGxlIGNvbW11dGluZyB0byBNY01hc3RlciBVbml2ZXJzaXR5IGluIENhbmFkYSBbQFdoYWxlbjIwMTNdLiBUaGUgZGF0YXNldCB3YXMgbG9hZGVkIGFib3ZlIGFzIHBhcnQgb2YgdGhlIHByZWxpbWluYXJpZXMgb2YgdGhpcyBjaGFwdGVyLiBXZSBjYW4gYmVnaW4gYnkgZXhwbG9yaW5nIHRoZSBkYXRhLiBQbGVhc2Ugbm90ZSB0aGF0IHRoaXMgaXMgdGhlIHNhbWUgZGF0YXNldCB0aGF0IHlvdSB1c2VkIGluIENoYXB0ZXIgXEByZWYoY2hhcHRlci0xKSwgYnV0IG5vdCB0aGUgc2FtZSBmaWxlLiBGb3IgY29udmVuaWVuY2UsIHRoZSBkYXRhc2V0IHdhcyBwcmUtcHJvY2Vzc2VkLiBUaGUgY29udGVudHMgb2YgdGhlIGRhdGFmcmFtZSBjYW4gYmUgcXVpY2tseSBzZWVuIGJ5IG1lYW5zIG9mIHRoZSBmdW5jdGlvbiBgaGVhZCgpYC4gVGhpcyBmdW5jdGlvbiB3aWxsIGRpc3BsYXkgdGhlIGZpcnN0IGZldyB0b3Agcm93cyBvZiB0aGUgZGF0YWZyYW1lOg0KYGBge3J9DQpoZWFkKG1jX2NvbW11dGUsIDgpDQpgYGANCg0KQXMgeW91IGNhbiBzZWUsIHRoZSBkYXRhZnJhbWUgaXMgaW4gd2lkZSBmb3JtYXQsIG1lYW5pbmcgdGhhdCBlYWNoIHJvdyByZXByZXNlbnRzIG9uZSBkZWNpc2lvbi1tYWtlciBhbmQgdGhlIGluZm9ybWF0aW9uIGFib3V0IHRoZSBjaG9pY2Ugc2l0dWF0aW9ucyBpcyBzcHJlYWQ6IGZvciBpbnN0YW5jZSwgdGhlcmUgaXMgb25lIHZhcmlhYmxlIGZvciB0cmF2ZWwgdGltZSwgYnV0IGl0IGFwcGVhcnMgaW4gZm91ciBjb2x1bW5zLCBvbmUgZm9yIGVhY2ggb2YgdGhlIGFsdGVybmF0aXZlcyAoIkN5Y2xlIiwgIldhbGsiLCAiSFNSIiwgYW5kICJDYXIiKS4gVGhlIHBhY2thZ2UgYG1sb2dpdGAgd29ya3Mgd2l0aCBfbG9uZ18gdGFibGVzLCB3aGVyZSBlYWNoIHJvdyBpcyBhIGNob2ljZSBzaXR1YXRpb24uIFNpbmNlIHdpZGUgdGFibGVzIGFyZSBtb3JlIGNvbW1vbiwgdGhlIHBhY2thZ2UgaW5jbHVkZXMgYSB1dGlsaXR5IGZ1bmN0aW9uIHRvIHJlc2hhcGUgdGhlIHRhYmxlLiBUaGlzIGlzIHVzZWQgYmVsb3cgdG8gY29udmVydCBvdXIgd2lkZSB0YWJsZSBpbnRvIGEgbG9uZyB0YWJsZS4gTm90aWNlIHRoYXQgd2UgbmVlZCB0byBpbmRpY2F0ZSB3aGljaCB2YXJpYWJsZXMgYXJlIF92YXJ5aW5nXywgbWVhbmluZyB0aGF0IHRoZXkgdmFyeSBieSBhbHRlcm5hdGl2ZS4gSW4gb3VyIHdpZGUgdGFibGUsIHRoZXJlIGFyZSBmb3VyIHZhcmlhYmxlcyB0aGF0IHZhcnkgYnkgYWx0ZXJuYXRpdmU6IHRyYXZlbCB0aW1lIChgdGltZWApLCBhY2Nlc3MgdGltZSAoYGFjY2Vzc2A6IHRoZSB0aW1lIG5lZWRlZCB0byByZWFjaCBhbiBIU1IgYnVzIHN0b3ApLCB3YWl0aW5nIHRpbWUgKGB3YWl0YDogdGltZSBzcGVudCB3YWl0aW5nIGZvciBhbiBIU1IgYnVzKSwgYW5kIG51bWJlciBvZiB0cmFuc2ZlcnMgd2hlbiB1c2luZyBIU1IgKGB0cmFuc2ZlcmApLiBUaGUgbGF0dGVyIHRocmVlIHZhcmlhYmxlcyBhcmUgc3BlY2lmaWMgdG8gSFNSIGFuZCB0aGVyZWZvcmUgYXJlIHNldCB0byB6ZXJvIGZvciB0aGUgbW9kZXMgIkNhciIsIEN5Y2xlIiwgYW5kICJXYWxrIi4NCmBgYHtyfQ0KbWNfY29tbXV0ZV9sb25nIDwtIG1sb2dpdC5kYXRhKG1jX2NvbW11dGUsIHNoYXBlID0gIndpZGUiLCBjaG9pY2UgPSAiY2hvaWNlIiwgdmFyeWluZyA9IDc6MjIpDQpgYGANCg0KSWYgd2UgZXhhbWluZSB0aGUgbG9uZyB0YWJsZSwgd2Ugc2VlIHRoYXQgaW5zdGVhZCBvZiBlYWNoIHJvdyBiZWluZyBhbiBpbmRpdmlkdWFsLCBlYWNoIHJvdyBpcyBhIGNob2ljZSBzaXR1YXRpb246DQpgYGB7cn0NCmhlYWQobWNfY29tbXV0ZV9sb25nLCA4KQ0KYGBgDQoNClNpbmNlIHRoZXJlIGFyZSBmb3VyIGFsdGVybmF0aXZlcyBpbiB0aGlzIGNhc2UsIGVhY2ggcm93IGNvcnJlc3BvbmRzIHRvIHRoZSBjaG9pY2Ugc2l0dWF0aW9uIGZvciBhbiBhbHRlcm5hdGl2ZSBmb3IgYW4gaW5kaXZpZHVhbC4gV2Ugbm90aWNlIHRoYXQgdGhlIHJvdyBuYW1lcyBub3cgaGF2ZSB0aGUgZm9ybWF0IGAjLkFsdGAsIHdoZXJlIGAjYCBpcyB0aGUgbnVtYmVyIG9mIHRoZSBkZWNpc2lvbi1tYWtlciBhbmQgYEFsdGAgaXMgdGhlIG5hbWUgb2YgdGhlIGFsdGVybmF0aXZlLiBJbiB0aGlzIHdheSB0aGUgZmlyc3QgZm91ciByb3dzIG9mIHRoZSB0YWJsZSBjb3JyZXNwb25kIHRvIHRoZSBmaXJzdCBkZWNpc2lvbi1tYWtlciB3aG8sIGZhY2VkIHdpdGggZm91ciBhbHRlcm5hdGl2ZXMsIGNob3NlIEhTUiAocHVibGljIHRyYW5zcG9ydGF0aW9uKSAtIGFzIHJlY29yZGVkIGluIHRoZSBjb2x1bW4gYGNob2ljZWAuIFRoZSBuZXh0IGZvdXIgcm93cyBjb3JyZXNwb25kIHRvIHRoZSBzZWNvbmQgZGVjaXNpb24tbWFrZXIgaW4gdGhlIHNhbXBsZSAod2hvIGFsc28gY2hvc2UgSFNSKSwgYW5kIHNvIG9uLCBmb3VyIHJvd3MgcGVyIGRlY2lzaW9uLW1ha2VyLiBNb3JlIGdlbmVyYWxseSwgdGhlcmUgd2lsbCBiZSAkSiQgcm93cyBwZXIgZGVjaXNpb24tbWFrZXIuIFdoZW4gYW4gYXR0cmlidXRlIGlzIG1pc3NpbmcsIHRoaXMgbWVhbnMgdGhhdCB0aGUgYWx0ZXJuYXRpdmUgd2FzIG5vdCBhdmFpbGFibGUgdG8gdGhlIGRlY2lzaW9uLW1ha2VyLiBGb3IgZXhhbXBsZToNCmBgYHtyfQ0Kc2VsZWN0KG1jX2NvbW11dGVfbG9uZywgdGltZSkgJT4lIGhlYWQoOCkNCmBgYA0KDQpIZXJlIHdlIHNlZSB0aGF0IHRoZSBtb2RlcyAiQ2FyIiBhbmQgIkN5Y2xlIiB3ZXJlIG5vdCBhdmFpbGFibGUgdG8gZGVjaXNpb24tbWFrZXIgMSwgYW5kICJDeWNsZSIgd2FzIG5vdCBhdmFpbGFibGUgdG8gZGVjaXNpb24tbWFrZXIgMi4NCg0KVGhlIGZpcnN0IHN0ZXAgdG93YXJkcyBkZXZlbG9waW5nIGEgY2hvaWNlIG1vZGVsIGlzIHRvIHNwZWNpZnkgdGhlIHV0aWxpdHkgZnVuY3Rpb25zIGZvciB0aGUgZGVzaXJlZCBtb2RlbC4gVGhlIHBhY2thZ2UgYG1sb2dpdGAgdXNlcyB0aGUgcGFja2FnZSBgbUZvcm11bGFgIHRvIGNyZWF0ZSB0aGUgZnVuY3Rpb25zLiBUaGlzIHBhY2thZ2UgY3JlYXRlcyBvYmplY3RzIHRoYXQgYnVpbGQgdXBvbiB0aGUgW2BGb3JtdWxhYCBwYWNrYWdlXShodHRwczovL0NSQU4uUi1wcm9qZWN0Lm9yZy9wYWNrYWdlPUZvcm11bGEgKSBmb3IgbXVsdGktY29tcG9uZW50IGZvcm11bGFzLiBBcyBzZWVuIGFib3ZlLCB1dGlsaXR5IGZ1bmN0aW9ucyBjYW4gcG90ZW50aWFsbHkgaGF2ZSBtdWx0aXBsZSBjb21wb25lbnRzLCBzbyB0aGUgZnVuY3Rpb25hbGl0eSB0byBidWlsZCBmb3JtdWxhcyBpbiBgbUZvcm11bGFgIGFuZCBgRm9ybXVsYWAgaXMgcXVpdGUgdXNlZnVsLg0KDQpGb3JtdWxhcyBmb3IgdGhlIGBtbG9naXRgIHBhY2thZ2UgYXJlIGRlZmluZWQgdXNpbmcgdGhyZWUgcGFydHM6DQoNCiQkDQpcdGV4dHtjaG9pY2V9IFxzaW0gXHRleHR7YWx0ZXJuYXRpdmUgc3BlY2lmaWMgdmFycyB3aXRoIGdlbmVyaWMgY29lZmZpY2llbnRzIH18XHRleHR7IGluZGl2aWR1YWwgc3BlY2lmaWMgdmFycyB9fFx0ZXh0eyBhbHRlcm5hdGl2ZSBzcGVjaWZpYyB2YXJzIHdpdGggc3BlY2lmaWMgY29lZmZpY2llbnRzfSAgDQokJA0KDQpJZiB3ZSBsaXN0IGFsbCBjb2x1bW5zIGluIHRoZSBkYXRhZnJhbWUsIHdlIGNhbiBzZWUgd2hhdCB2YXJpYWJsZXMgYXJlIGF2YWlsYWJsZSBmb3IgdGhpcyBhbmFseXNpczoNCmBgYHtyfQ0KY29sbmFtZXMobWNfY29tbXV0ZV9sb25nKQ0KYGBgDQoNCkJlc2lkZXMgaWRlbnRpZmllciB2YXJpYWJsZXMgYGlkYCBhbmQgYGNoaWRgLCBhbmQgdGhlIHZhcmlhYmxlIGZvciBgY2hvaWNlYCwgd2Ugc2VlIHRoYXQgc2V2ZXJhbCB2YXJpYWJsZXMgYXJlIHNwZWNpZmljIHRvIHRoZSBpbmRpdmlkdWFsIGRlY2lzaW9uLW1ha2Vycy4gVGhlc2UgYXJlIGBwYXJraW5nYCAoYXZhaWxhYmlsaXR5IG9mIGEgcGFya2luZyBwYXNzKSwgYHZlaGluZGAgKHdoZXRoZXIgdGhlIGRlY2lzaW9uLW1ha2VyIGhhZCBpbmRpdmlkdWFsIGFjY2VzcyB0byBhIHByaXZhdGUgdmVoaWNsZSksIGBnZW5kZXJgLCBgYWdlYCwgYHNoYXJlZGAgKGxpdmluZyBpbiBzaGFyZWQgYWNjb21tb2RhdGlvbnMgYXdheSBmcm9tIHRoZSBmYW1pbHkgaG9tZSksIGBmYW1pbHlgIChsaXZpbmcgYXQgdGhlIGZhbWlseSBob21lKSwgYW5kIGBjaGlsZGAgKHJlc3BvbmRlbnQgd2FzIHJlc3BvbnNpYmxlIGZvciBhdCBsZWFzdCBvbmUgbWlub3JzIGluIHRoZSBob3VzZWhvbGQpLiBGdXJ0aGVybW9yZSwgc29tZSB2YXJpYWJsZXMgcmVsYXRlIHRvIHRoZSBwaHlzaWNhbCBlbnZpcm9ubWVudCBvZiB0aGUgcGxhY2Ugb2YgcmVzaWRlbmNlIChgc3RyZWV0X2RlbnNpdHlgIGFuZCBgc2lkZXdhbGtfZGVuc2l0eWApLCBpbiBhZGRpdGlvbiB0byB0aGUgY29vcmRpbmF0ZXMgb2YgdGhlIHBsYWNlIG9mIHJlc2lkZW5jZSAoZ2VvY29kZWQgdG8gdGhlIG5lYXJlc3QgbWFqb3IgaW50ZXJzZWN0aW9uIG9yIHBvc3RhbCBjb2RlIGNlbnRyb2lkKS4gT25lIHZhcmlhYmxlIGlzIGFsdGVybmF0aXZlIHNwZWNpZmljLCBuYW1lbHkgYHRpbWVgICh0cmF2ZWwgdGltZSBpbiBtaW51dGVzKS4gQW5kLCBhcyBub3RlZCBiZWZvcmUsIHRocmVlIHZhcmlhYmxlcyBhcmUgc3BlY2lmaWMgdG8gcHVibGljIHRyYW5zcG9ydGF0aW9uLCBuYW1lbHkgYGFjY2Vzc2AgKGFjY2VzcyB0aW1lIHRvIHB1YmxpYyB0cmFuc3BvcnRhdGlvbiBpbiBtaW51dGVzKSwgYHdhaXRgICh3YWl0aW5nIHRpbWUgaW4gbWludXRlcyksIGFuZCBgdHJhbnNmZXJgIChudW1iZXIgb2YgdHJhbnNmZXJzIHdoZW4gdHJhdmVsaW5nIGJ5IHB1YmxpYyB0cmFuc3BvcnRhdGlvbikuDQoNCldlIGNhbiBiZWdpbiBieSBkZWZpbmluZyBhIHZlcnkgc2ltcGxlIGZvcm11bGEgdGhhdCBjb25zaWRlcnMgb25seSB0cmF2ZWwgdGltZS4gV2Ugd2lsbCBzYXZlIHRoaXMgb2JqZWN0IGFzIGBmMWA6DQpgYGB7cn0NCmYxIDwtIG1Gb3JtdWxhKGNob2ljZSB+IHRpbWUpDQpgYGANCg0KVGhlIGZ1bmN0aW9uIGBtb2RlbC5tYXRyaXhgIGFsbG93cyB1cyB0byBzZWUgaG93IHRoZSBmb3JtdWxhIGlzIGFwcGxpZWQgdG8gdGhlIGRhdGEgKHdlIHVzZSBgaGVhZCgpYCB0byBkaXNwbGF5IG9ubHkgdGhlIHRvcCByb3dzIG9mIHRoZSBtb2RlbCBtYXRyaXgpOg0KYGBge3J9DQpoZWFkKG1vZGVsLm1hdHJpeChmMSwgbWNfY29tbXV0ZV9sb25nLCA4KSkNCmBgYA0KDQpXZSBjYW4gc2VlIHRoYXQgdGhlIGZvcm11bGEgaW5jbHVkZXMgYnkgZGVmYXVsdCB0aGUgYWx0ZXJuYXRpdmUgc3BlY2lmaWMgY29lZmZpY2llbnRzLCBpbiB0aGlzIGNhc2UgdXNpbmcgY2FyIGFzIHRoZSByZWZlcmVuY2UgYWx0ZXJuYXRpdmUuIFRoZSBjb3JyZXNwb25kaW5nIHV0aWxpdHkgZnVuY3Rpb25zIGFyZSBhcyBmb2xsb3dzOg0KDQokJA0KXGJlZ2lue2FycmF5fXtsfQ0KICAgICAgICAgICAgICAgIFZfe2lcdGV4dHtDeWNsZX19ID1cXA0KICAgICAgICAgICAgICAgIFZfe2lcdGV4dHtXYWxrfX0gPVxcDQogICAgICAgICAgICAgICAgVl97aVx0ZXh0e0hTUn19ID1cXA0KICAgICAgICAgICAgICAgIFZfe2lcdGV4dHtIU1J9fSA9XFwNCiAgICAgICAgICAgICAgXGVuZHthcnJheX0gIA0KICBcb3ZlcmJyYWNleyBcYmVnaW57YXJyYXl9e2xsbH0NCiAgICAgICAgICAgICAgICAwICYgKzAgJiArMFxcDQogICAgICAgICAgICAgICAgXG11X3tcdGV4dHtXYWxrfX0gJiArMCAmICswXFwNCiAgICAgICAgICAgICAgICAwICYgK1xtdV97XHRleHR7SFNSfX0gJiArMCBcXA0KICAgICAgICAgICAgICAgIDAgJiArMCAmICtcbXVfe1x0ZXh0e0Nhcn19XFwNCiAgICAgICAgICAgICAgXGVuZHthcnJheX0NCiAgICAgICAgICAgICAgfV5cdGV4dHthbHRlcm5hdGl2ZSBzcGVjaWZpYyBjb25zdGFudHN9IA0KICBcdW5kZXJicmFjZXtcYmVnaW57YXJyYXl9e2xsbH0NCiAgICAgICAgICAgICAgICArXGJldGFfMVx0ZXh0e3RpbWV9X3tpXHRleHR7Q3ljbGV9fVxcDQogICAgICAgICAgICAgICAgK1xiZXRhXzFcdGV4dHt0aW1lfV97aVx0ZXh0e1dhbGt9fVxcDQogICAgICAgICAgICAgICAgK1xiZXRhXzFcdGV4dHt0aW1lfV97aVx0ZXh0e0hTUn19XFwNCiAgICAgICAgICAgICAgICArXGJldGFfMVx0ZXh0e3RpbWV9X3tpXHRleHR7Q2FyfX1cXA0KICAgICAgICAgICAgICBcZW5ke2FycmF5fQ0KICAgICAgICAgICAgICB9X3tcdGV4dHthbHRlcm5hdGl2ZSB2YXJzLiB3aXRoIGdlbmVyaWMgY29lZmZpY2llbnRzfX0NCiQkDQoNCkRlZmluZSBub3cgYSBmb3JtdWxhIHdpdGggYW4gaW5kaXZpZHVhbC1zcGVjaWZpYyB2YXJpYWJsZSwgc2F5IHNpZGV3YWxrIGRlbnNpdHkgYXQgdGhlIHBsYWNlIG9mIHJlc2lkZW5jZSwgYW5kIGNhbGwgaXQgYGYyYDoNCmBgYHtyfQ0KZjIgPC0gbUZvcm11bGEoY2hvaWNlIH4gdGltZSB8IHNpZGV3YWxrX2RlbnNpdHkpDQpgYGANCg0KVGhlIG1vZGVsIG1hdHJpeCBpcyBub3c6DQpgYGB7cn0NCmhlYWQobW9kZWwubWF0cml4KGYyLCBtY19jb21tdXRlX2xvbmcpKQ0KYGBgDQoNCkFuZCB0aGUgdXRpbGl0eSBmdW5jdGlvbnMgYXJlIHRoZXJlZm9yZToNCg0KJCQNClxiZWdpbnthcnJheX17bH0NCiAgICAgICAgICAgICAgICBWX3tpXHRleHR7Q3ljbGV9fSA9XFwNCiAgICAgICAgICAgICAgICBWX3tpXHRleHR7V2Fsa319ID1cXA0KICAgICAgICAgICAgICAgIFZfe2lcdGV4dHtIU1J9fSA9XFwNCiAgICAgICAgICAgICAgICBWX3tpXHRleHR7SFNSfX0gPVxcDQogICAgICAgICAgICAgIFxlbmR7YXJyYXl9ICANCiAgXG92ZXJicmFjZXsgXGJlZ2lue2FycmF5fXtsbGx9DQogICAgICAgICAgICAgICAgMCAmICswICYgKzBcXA0KICAgICAgICAgICAgICAgIFxtdV97XHRleHR7V2Fsa319ICYgKzAgJiArMFxcDQogICAgICAgICAgICAgICAgMCAmICtcbXVfe1x0ZXh0e0hTUn19ICYgKzAgXFwNCiAgICAgICAgICAgICAgICAwICYgKzAgJiArXG11X3tcdGV4dHtDYXJ9fVxcDQogICAgICAgICAgICAgIFxlbmR7YXJyYXl9DQogICAgICAgICAgICAgIH1eXHRleHR7YWx0ZXJuYXRpdmUgc3BlY2lmaWMgY29uc3RhbnRzfSANCiAgXHVuZGVyYnJhY2V7XGJlZ2lue2FycmF5fXtsbGx9DQogICAgICAgICAgICAgICAgK1xiZXRhXzFcdGV4dHt0aW1lfV97aVx0ZXh0e0N5Y2xlfX1cXA0KICAgICAgICAgICAgICAgICtcYmV0YV8xXHRleHR7dGltZX1fe2lcdGV4dHtXYWxrfX1cXA0KICAgICAgICAgICAgICAgICtcYmV0YV8xXHRleHR7dGltZX1fe2lcdGV4dHtIU1J9fVxcDQogICAgICAgICAgICAgICAgK1xiZXRhXzFcdGV4dHt0aW1lfV97aVx0ZXh0e0Nhcn19XFwNCiAgICAgICAgICAgICAgXGVuZHthcnJheX0NCiAgICAgICAgICAgICAgfV97XHRleHR7YWx0ZXJuYXRpdmUgdmFycy4gd2l0aCBnZW5lcmljIGNvZWZmaWNpZW50c319DQogIFxvdmVyYnJhY2V7IFxiZWdpbnthcnJheX17bGxsfQ0KICAgICAgICAgICAgICAgIDAgJiArMCAmICswXFwNCiAgICAgICAgICAgICAgICBcZ2FtbWFfezF9XHRleHR7c3dkfV97aX0gJiArMCAmICswXFwNCiAgICAgICAgICAgICAgICAwICYgKyBcZ2FtbWFfezJ9XHRleHR7c3dkfV97aX0gJiArMCBcXA0KICAgICAgICAgICAgICAgIDAgJiArMCAmICtcZ2FtbWFfezN9XHRleHR7c3dkfV97aX1cXA0KICAgICAgICAgICAgICBcZW5ke2FycmF5fQ0KICAgICAgICAgICAgICB9Xlx0ZXh0e2luZGl2aWR1YWwgdmFycyB3aXRoIHNwZWNpZmljIGNvZWZmaWNpZW50c30gDQokJA0KDQpIZXJlLCB3ZSB0cnkgYSBkaWZmZXJlbnQgZm9ybXVsYSwgd2hlcmUgdGltZSBoYXMgYWx0ZXJuYXRpdmUtc3BlY2lmaWMgaW5zdGVhZCBvZiBnZW5lcmljIGNvZWZmaWNpZW50cywgYW5kIGNhbGwgaXQgYGYzYDoNCmBgYHtyfQ0KZjMgPC0gbUZvcm11bGEoY2hvaWNlIH4gMCB8IHNpZGV3YWxrX2RlbnNpdHkgfCB0aW1lKQ0KYGBgDQoNCk5vdGUgdGhhdCwgc2luY2Ugd2UgZG8gbm90IGRlZmluZSBvdGhlciBhbHRlcm5hdGl2ZS1zcGVjaWZpYyB2YXJpYWJsZXMgd2l0aCBnZW5lcmljIGNvZWZmaWNpZW50cywgd2UgaGF2ZSB0byBleHBsaWNpdGx5IHN0YXRlIHRoYXQgdGhlcmUgYXJlIGAwYCBzdWNoIHZhcmlhYmxlcyENCg0KVGhpcyBmb3JtdWxhIGxlYWRzIHRvIHRoZSBmb2xsb3dpbmcgbW9kZWwgbWF0cml4Og0KYGBge3J9DQpoZWFkKG1vZGVsLm1hdHJpeChmMywgbWNfY29tbXV0ZV9sb25nKSkNCmBgYA0KDQpUaGUgdXRpbGl0eSBmdW5jdGlvbnMgZm9yIHRoaXMgYXJlOg0KDQokJA0KXGJlZ2lue2FycmF5fXtsfQ0KICAgICAgICAgICAgICAgIFZfe2lcdGV4dHtDeWNsZX19ID1cXA0KICAgICAgICAgICAgICAgIFZfe2lcdGV4dHtXYWxrfX0gPVxcDQogICAgICAgICAgICAgICAgVl97aVx0ZXh0e0hTUn19ID1cXA0KICAgICAgICAgICAgICAgIFZfe2lcdGV4dHtIU1J9fSA9XFwNCiAgICAgICAgICAgICAgXGVuZHthcnJheX0gIA0KICBcb3ZlcmJyYWNleyBcYmVnaW57YXJyYXl9e2xsbH0NCiAgICAgICAgICAgICAgICAwICYgKzAgJiArMFxcDQogICAgICAgICAgICAgICAgXG11X3tcdGV4dHtXYWxrfX0gJiArMCAmICswXFwNCiAgICAgICAgICAgICAgICAwICYgK1xtdV97XHRleHR7SFNSfX0gJiArMCBcXA0KICAgICAgICAgICAgICAgIDAgJiArMCAmICtcbXVfe1x0ZXh0e0Nhcn19XFwNCiAgICAgICAgICAgICAgXGVuZHthcnJheX0NCiAgICAgICAgICAgICAgfV5cdGV4dHthbHRlcm5hdGl2ZSBzcGVjaWZpYyBjb25zdGFudHN9IA0KICBcdW5kZXJicmFjZXsgXGJlZ2lue2FycmF5fXtsbGx9DQogICAgICAgICAgICAgICAgMCAmICswICYgKzBcXA0KICAgICAgICAgICAgICAgIFxnYW1tYV97MX1cdGV4dHtzd2R9X3tpfSAmICswICYgKzBcXA0KICAgICAgICAgICAgICAgIDAgJiArIFxnYW1tYV97Mn1cdGV4dHtzd2R9X3tpfSAmICswIFxcDQogICAgICAgICAgICAgICAgMCAmICswICYgK1xnYW1tYV97M31cdGV4dHtzd2R9X3tpfVxcDQogICAgICAgICAgICAgIFxlbmR7YXJyYXl9DQogICAgICAgICAgICAgIH1fXHRleHR7aW5kaXZpZHVhbCB2YXJzIHdpdGggc3BlY2lmaWMgY29lZmZpY2llbnRzfSANCiAgXG92ZXJicmFjZXtcYmVnaW57YXJyYXl9e2xsbH0NCiAgICAgICAgICAgICAgICArXGRlbHRhXzFcdGV4dHt0aW1lfV97aVx0ZXh0e0N5Y2xlfX0gJiArMCAmICswICYgKzBcXA0KICAgICAgICAgICAgICAgICswICYgK1xkZWx0YV8yXHRleHR7dGltZX1fe2lcdGV4dHtXYWxrfX0gJiArMCAmICswXFwNCiAgICAgICAgICAgICAgICArMCAmICswICYgICtcZGVsdGFfM1x0ZXh0e3RpbWV9X3tpXHRleHR7SFNSfX1cXA0KICAgICAgICAgICAgICAgICswICYgKzAgJiArMCAmICtcZGVsdGFfNFx0ZXh0e3RpbWV9X3tpXHRleHR7Q2FyfX1cXA0KICAgICAgICAgICAgICBcZW5ke2FycmF5fQ0KICAgICAgICAgICAgICB9XntcdGV4dHthbHRlcm5hdGl2ZSB2YXJzLiB3aXRoIHNwZWNpZmljIGNvZWZmaWNpZW50c319DQoNCiQkDQoNCkdpdmVuIHRoZSB1dGlsaXR5IGZ1bmN0aW9ucywgdGhlIGxvZ2l0IHByb2JhYmlsaXRpZXMgZm9yIGVhY2ggYWx0ZXJuYXRpdmUgYXJlOg0KDQokJA0KXGJlZ2lue2FycmF5fXtsfQ0KICAgIFAoXHRleHR7Q3ljbGV9KSA9IFxmcmFje2Vee1Zfe1x0ZXh0e0N5Y2xlfX19fXtlXntWX3tcdGV4dHtDeWNsZX19fStlXntWX3tcdGV4dHtXYWxrfX19K2Vee1Zfe1x0ZXh0e0hTUn19fStlXntWX3tcdGV4dHtDYXJ9fX19XFwNCiAgICBQKFx0ZXh0e1dhbGt9KSA9IFxmcmFje2Vee1Zfe1x0ZXh0e1dhbGt9fX19e2Vee1Zfe1x0ZXh0e0N5Y2xlfX19K2Vee1Zfe1x0ZXh0e1dhbGt9fX0rZV57Vl97XHRleHR7SFNSfX19K2Vee1Zfe1x0ZXh0e0Nhcn19fX1cXA0KICAgIFAoXHRleHR7SFNSfSkgPSBcZnJhY3tlXntWX3tcdGV4dHtDeWNsZX19fX17ZV57Vl97XHRleHR7Q3ljbGV9fX0rZV57Vl97XHRleHR7V2Fsa319fStlXntWX3tcdGV4dHtIU1J9fX0rZV57Vl97XHRleHR7Q2FyfX19fVxcDQogICAgUChcdGV4dHtDYXJ9KSA9MSAtIFAoXHRleHR7Q3ljbGV9KSAtIFAoXHRleHR7V2Fsa30pIC0gUChcdGV4dHtIU1J9KVxcDQpcZW5ke2FycmF5fQ0KJCQNCg0KVGhlIHV0aWxpdHkgZnVuY3Rpb25zIGRlcGVuZCBvbiB0aGUgZGF0YSBidXQgYWxzbyBvbiB0aGUgY29lZmZpY2llbnRzLCB3aGljaCB3ZSBkbyBub3Qga25vdyBfYSBwcmlvcmlfLiBSYXRoZXIsIHRoZXNlIG11c3QgYmUgcmV0cmlldmVkIGZyb20gdGhlIHNhbXBsZSwgYXMgZGlzY3Vzc2VkIG5leHQuDQoNCiMjIEVzdGltYXRpb24NCg0KQmVmb3JlIHdlIGNhbiBjYWxjdWxhdGUgdGhlIGNob2ljZSBwcm9iYWJpbGl0aWVzLCB3ZSBuZWVkIHRvIHNvbWVob3cgb2J0YWluIGNvZWZmaWNpZW50cyBmb3IgdGhlIHV0aWxpdHkgZnVuY3Rpb25zLiBUaGUgcHJvY2VzcyB0byBkbyBzbyBpcyBjYWxsZWQgX2VzdGltYXRpb25fLCBhbmQgaXQgaW52b2x2ZXMgdGhlIHVzZSBvZiBhIHN0YXRpc3RpY2FsIHNhbXBsZS4gDQoNClRvIGVzdGltYXRlIHRoZSBjb2VmZmljaWVudHMgb2YgYSBtb2RlbCB3ZSBuZWVkIHRvIGRlZmluZSBhIGNyaXRlcmlvbiB0aGF0IHdlIHdpc2ggdG8gc2F0aXNmeSB3aXRoIG91ciBjaG9pY2Ugb2YgY29lZmZpY2llbnRzLiBFc3RpbWF0ZXMgY2FuIHRha2UgYW4gaW5maW5pdGUgbnVtYmVyIG9mIHZhbHVlcywgYWZ0ZXIgYWxsLCBzbyBvdXIgY3JpdGVyaW9uIG11c3QgYmUgb3B0aW1hbCBpbiBzb21lIHNlbnNlIC0gaW4gdGhpcyB3YXksIG9uY2UgdGhhdCB3ZSBlc3RpbWF0ZSB0aGUgY29lZmZpY2llbnRzIHdlIGNhbiBiZSBzYXRpc2ZpZWQgdGhhdCB0aGV5IGFyZSB0aGUgYmVzdCB0aGF0IHdlIGNhbiBvYnRhaW4gZm9yIHRoZSBtb2RlbCB1bmRlciBjb25zaWRlcmF0aW9ucywgZ2l2ZW4gdGhlbiBpbnB1dHMuDQoNCkEgY29tbW9uIGNyaXRlcmlvbiB1c2VkIHRvIGVzdGltYXRlIGRpc2NyZXRlIGNob2ljZSBtb2RlbHMgaXMgdGhlIF9saWtlbGlob29kXy4gU28gd2hhdCBpcyB0aGlzIGxpa2VsaWhvb2Q/IFByZXZpb3VzbHkgd2UgZW5jb3VudGVyZWQgcHJvYmFiaWxpdHkgZGlzdHJpYnV0aW9uIGZ1bmN0aW9ucy4gVGhlc2UgZnVuY3Rpb25zIHdlcmUgZGVmaW5lZCBieSBwYXJhbWV0ZXJzIChzdWNoIGFzIHRoZSBsb2NhdGlvbiBwYXJhbWV0ZXIgYW5kIHRoZSBkaXNwZXJzaW9uIHBhcmFtZXRlcikuIEdpdmVuIHRoZSBwYXJhbWV0ZXJzLCBpdCBpcyBwb3NzaWJsZSB0byBjYWxjdWxhdGUgdGhlIHByb2JhYmlsaXR5IG9mIHZhbHVlcyBmb3IgYSB2YXJpYWJsZSAkeCQuIEEgbGlrZWxpaG9vZCBmdW5jdGlvbiBpcyBhIHNpbWlsYXIgY29uY2VwdCwgZXhjZXB0IHRoYXQgd2hlcmVhcyBpbiB0aGUgcHJvYmFiaWxpdHkgZnVuY3Rpb25zIHRoZSBwYXJhbWV0ZXJzIHdlcmUgZ2l2ZW4sIGluIGEgbGlrZWxpaG9vZCBmdW5jdGlvbiB0aGUgZGF0YSBhcmUgZ2l2ZW4gYW5kIHRoZSBwYXJhbWV0ZXJzIG5lZWQgdG8gYmUgb2J0YWluZWQgZnJvbSB0aGUgZnVuY3Rpb24uDQoNClRoZSByZWxldmFudCBsaWtlbGlob29kIGZ1bmN0aW9uIGZvciB0aGUgbXVsdGlub21pYWwgbG9naXQgbW9kZWwgaXMgYXMgZm9sbG93czoNCiQkDQpMID0gXHByb2Rfe2k9bn1eTlxwcm9kX3tqPTF9XkogUF97aWp9Xnt5X3tpan19DQokJA0Kd2hlcmUgJFBfe2lqfSQgaXMgdGhlIHByb2JhYmlsaXR5IG9mIGRlY2lzaW9uLW1ha2VyICRpJCBzZWxlY3RpbmcgYWx0ZXJuYXRpdmUgJGokIGFuZCAkeV97aWp9JCBpcyBhbiBpbmRpY2F0b3IgdmFyaWFibGUgdGhhdCB0YWtlcyB0aGUgdmFsdWUgb2YgJDEkIGlmIGluZGl2aWR1YWwgJGkkIGNob3NlIGFsdGVybmF0aXZlICRqJCBhbmQgJDAkIG90aGVyd2lzZS4gVGhlIGVmZmVjdCBvZiB0aGUgaW5kaWNhdG9yIHZhcmlhYmxlIGlzIHRvIHR1cm4gdGhlIHByb2JhYmlsaXRpZXMgb24gYW5kIG9mZiwgc2luY2UgJFBeMCA9IDEkIGFuZCAkUF4xID0gUCQuIE5vdGljZSB0aGF0IHRoZSBsaWtlbGlob29kIGZ1bmN0aW9uIGlzIGJvdW5kZWQgYmV0d2VlbiAwIGFuZCAxLCBidXQgaW4gdGhlIGNhc2Ugb2YgdGhlIGxvZ2l0IG1vZGVsIGl0IGlzIG5ldmVyIGV4YWN0bHkgemVybyBub3Igb25lLCBzaW5jZSB0aGUgbG9naXQgcHJvYmFiaWxpdGllcyBuZXZlciB0YWtlIGFueSBvZiB0aG9zZSBleGFjdCB2YWx1ZXMuDQoNCldlIGNhbiBleHBsb3JlIHRoZSBiZWhhdmlvciBvZiB0aGUgbGlrZWxpaG9vZCBmdW5jdGlvbiBieSBtZWFucyBvZiBhIHNpbXBsZSBleGFtcGxlLiBDb25zaWRlciBhIGJpbm9taWFsIGxvZ2l0IG1vZGUsIHRoYXQgaXMsIGEgbW9kZWwgd2l0aCBvbmx5IHR3byBhbHRlcm5hdGl2ZXMgaW4gdGhlIGNob2ljZSBzZXQuIFRoZSBsaWtlbGlob29kIGZ1bmN0aW9uIG9mIHRoaXMgbW9kZWwgaXMgYXMgZm9sbG93czoNCg0KJCQNCkwgPSBccHJvZF97aT1ufV5OIFBfe2lBfV57eV97aUF9fVBfe2lCfV57eV97aUJ9fSA9IFxwcm9kX3tpPW59Xk4gDQogICAgXEJpZ2coXGZyYWN7ZV57Vl97aUF9fX17ZV57Vl97aUF9fSArIGVee1Zfe2lCfX19XEJpZ2cpXnt5X3tpQX19DQogICAgXEJpZ2coXGZyYWN7ZV57Vl97aUJ9fX17ZV57Vl97aUF9fSArIGVee1Zfe2lCfX19XEJpZ2cpXnt5X3tpQn19DQokJA0KDQpUaGUgdXRpbGl0eSBmdW5jdGlvbnMgJFZfe2lBfSQgYW5kICRWX3tpQn0kIGRlcGVuZCBvbiB0aGUgZGF0YSwgd2hpY2ggd2Uga25vdyAoc2luY2Ugd2UgaGF2ZSBhIHN0YXRpc3RpY2FsIHNhbXBsZSksIGFuZCB0aGUgY29lZmZpY2llbnRzLCB3aGljaCB3ZSBkbyBub3Qga25vdy4gDQoNCkZvciB0aGUgZXhhbXBsZSwgd2UgaGF2ZSB0aGUgZm9sbG93aW5nIHRveSBzYW1wbGUgd2l0aCBzaXggaW5kaXZpZHVhbHM6DQpgYGB7ciBlY2hvPUZBTFNFfQ0KdHMgPC0gZGF0YS5mcmFtZShJbmRpdmlkdWFsID0gYygxLCAyLCAzLCA0LCA1LCA2KSwNCiAgICAgICAgICAgICAgICAgQ2hvaWNlID0gYygiQSIsICJBIiwgIkIiLCAiQSIsICJCIiwgIkIiKSwgDQogICAgICAgICAgICAgICAgIHlpQSA9IGMoMSwgMSwgMCwgMSwgMCwgMCksDQogICAgICAgICAgICAgICAgIHlpQiA9IGMoMCwgMCwgMSwgMCwgMSwgMSksDQogICAgICAgICAgICAgICAgIHhpQSA9IGMoNSwgMiwgNSwgMSwgNCwgMyksDQogICAgICAgICAgICAgICAgIHhpQiA9IGMoNCwgNSwgMiwgNiwgMSwgNCkpDQogIA0Ka2FibGUodHMsICJodG1sIikgJT4lDQogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIikpDQpgYGANCg0KQmFzZWQgb24gdGhpcyBzYW1wbGUsIHdlIGNhbiBzcGVjaWZ5IHRoZSB1dGlsaXR5IGZ1bmN0aW9ucyBpbiB0aGlzIGZhc2hpb246DQoNCiQkDQpcYmVnaW57YXJyYXl9e2x9DQogICAgICAgICAgICAgICAgVl97aUF9ID0gMCAmKyYgXGJldGEgeF97aUF9XFwNCiAgICAgICAgICAgICAgICBWX3tpQn0gPSBcbXUgJismIFxiZXRhIHhfe2lCfVxcDQogICAgICAgICAgICAgIFxlbmR7YXJyYXl9ICANCiQkDQoNClRoZXNlIHV0aWxpdHkgZnVuY3Rpb25zIGFyZSB2ZXJ5IHNpbWlsYXIgdG8gdGhlIGZpcnN0IHNldCBvZiB1dGlsaXR5IGZ1bmN0aW9ucyB0aGF0IHdlIGRlZmluZWQgaW4gdGhlIHByZWNlZGluZyBzZWN0aW9uIGZvciB0aGUgY2FzZSBvZiBtb2RlIGNob2ljZS4NCg0KTmV4dCwgdGhlIGxpa2VsaWhvb2QgZnVuY3Rpb24gZm9yIHRoaXMgdG95IHNhbXBsZSBjYW4gYmUgd3JpdGVuIGFzIGEgZnVuY3Rpb24gb2YgJFxtdSQgYW5kICRcYmV0YSQuIEluIHRoaXMgd2F5LCBpdCBpcyBwb3NzaWJsZSB0byBjYWxjdWxhdGUgYW4gaW5pdGlhbCB2YWx1ZSBvZiB0aGUgbGlrZWxpaG9vZCBmdW5jdGlvbiBidSBzZXR0aW5nICRcbXUkIGFuZCAkXGJldGEkIHRvIHplcm8uIFdlIHdpbGwgY2FsbCB0aGlzICJFeHBlcmltZW50IDEiOg0KYGBge3J9DQptdSA8LSAwDQpiZXRhIDwtIDANCg0KUDFBXzEgPC0gKGV4cChiZXRhICogdHMkeGlBWzFdKS8oZXhwKGJldGEgKiB0cyR4aUFbMV0pICsgZXhwKG11ICsgYmV0YSAqIHRzJHhpQlsxXSkpKQ0KUDFCXzEgPC0gKGV4cChtdSArIGJldGEgKiB0cyR4aUJbMV0pLyhleHAoYmV0YSAqIHRzJHhpQVsxXSkgKyBleHAobXUgKyBiZXRhICogdHMkeGlCWzFdKSkpDQpQMkFfMSA8LSAoZXhwKGJldGEgKiB0cyR4aUFbMl0pLyhleHAoYmV0YSAqIHRzJHhpQVsyXSkgKyBleHAobXUgKyBiZXRhICogdHMkeGlCWzJdKSkpDQpQMkJfMSA8LSAoZXhwKG11ICsgYmV0YSAqIHRzJHhpQlsyXSkvKGV4cChiZXRhICogdHMkeGlBWzJdKSArIGV4cChtdSArIGJldGEgKiB0cyR4aUJbMl0pKSkNClAzQV8xIDwtIChleHAoYmV0YSAqIHRzJHhpQVszXSkvKGV4cChiZXRhICogdHMkeGlBWzNdKSArIGV4cChtdSArIGJldGEgKiB0cyR4aUJbM10pKSkNClAzQl8xIDwtIChleHAobXUgKyBiZXRhICogdHMkeGlCWzNdKS8oZXhwKGJldGEgKiB0cyR4aUFbM10pICsgZXhwKG11ICsgYmV0YSAqIHRzJHhpQlszXSkpKQ0KUDRBXzEgPC0gKGV4cChiZXRhICogdHMkeGlBWzRdKS8oZXhwKGJldGEgKiB0cyR4aUFbNF0pICsgZXhwKG11ICsgYmV0YSAqIHRzJHhpQls0XSkpKQ0KUDRCXzEgPC0gKGV4cChtdSArIGJldGEgKiB0cyR4aUJbNF0pLyhleHAoYmV0YSAqIHRzJHhpQVs0XSkgKyBleHAobXUgKyBiZXRhICogdHMkeGlCWzRdKSkpDQpQNUFfMSA8LSAoZXhwKGJldGEgKiB0cyR4aUFbNV0pLyhleHAoYmV0YSAqIHRzJHhpQVs1XSkgKyBleHAobXUgKyBiZXRhICogdHMkeGlCWzVdKSkpDQpQNUJfMSA8LSAoZXhwKG11ICsgYmV0YSAqIHRzJHhpQls1XSkvKGV4cChiZXRhICogdHMkeGlBWzVdKSArIGV4cChtdSArIGJldGEgKiB0cyR4aUJbNV0pKSkNClA2QV8xIDwtIChleHAoYmV0YSAqIHRzJHhpQVs2XSkvKGV4cChiZXRhICogdHMkeGlBWzZdKSArIGV4cChtdSArIGJldGEgKiB0cyR4aUJbNl0pKSkNClA2Ql8xIDwtIChleHAobXUgKyBiZXRhICogdHMkeGlCWzZdKS8oZXhwKGJldGEgKiB0cyR4aUFbNl0pICsgZXhwKG11ICsgYmV0YSAqIHRzJHhpQls2XSkpKQ0KICANCkwgPC0gIFAxQV8xXnRzJHlpQVsxXSAqIFAxQl8xXnRzJHlpQlsxXSAqIA0KICBQMkFfMV50cyR5aUFbMl0gKiBQMkJfMV50cyR5aUJbMl0gKiANCiAgUDNBXzFedHMkeWlBWzNdICogUDNCXzFedHMkeWlCWzNdICogDQogIFA0QV8xXnRzJHlpQVs0XSAqIFA0Ql8xXnRzJHlpQls0XSAqIA0KICBQNUFfMV50cyR5aUFbNV0gKiBQNUJfMV50cyR5aUJbNV0gKiANCiAgUDZBXzFedHMkeWlBWzZdICogUDZCXzFedHMkeWlCWzZdIA0KDQojIENyZWF0ZSBkYXRhIGZyYW1lIHRvIHRhYnVsYXRlIHJlc3VsdHM6DQpkZiA8LSBkYXRhLmZyYW1lKEluZGl2aWR1YWwgPSBjKDEsIDIsIDMsIDQsIDUsIDYpLA0KICAgICAgICAgICAgICAgICBDaG9pY2UgPSBjKCJBIiwgIkEiLCAiQiIsICJBIiwgIkIiLCAiQiIpLA0KICAgICAgICAgICAgICAgICBQQSA9IGMoUDFBXzEsIFAyQV8xLCBQM0FfMSwgUDRBXzEsIFA1QV8xLCBQNkFfMSksDQogICAgICAgICAgICAgICAgIFBCID0gYyhQMUJfMSwgUDJCXzEsIFAzQl8xLCBQNEJfMSwgUDVCXzEsIFA2Ql8xKSkNCg0Ka2FibGUoZGYsICJodG1sIiwgZGlnaXRzID0gNCwgYWxpZ24gPSAiYyIpICU+JQ0KICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIpKSAlPiUNCiAgZm9vdG5vdGUoZ2VuZXJhbCA9IHBhc3RlKCJUaGUgdmFsdWUgb2YgdGhlIGxpa2VsaWhvb2QgZnVuY3Rpb24gaXMgIiwgcm91bmQoTCwgZGlnaXRzID0gNCkpKQ0KYGBgDQoNCkFzIHlvdSBjYW4gc2VlLCB0aGF0IHRoZSBsb2dpdCBwcm9iYWJpbGl0aWVzIHdoZW4gYWxsIGNvZWZmaWNpZW50cyBhcmUgemVybyBpcyAkMC41JC4gQnkgc2V0dGluZyB0aGUgY29lZmZpY2llbnRzIHRvIHplcm8gd2UgaGF2ZSBkZWZpbmVkIHdoYXQgaXMgY2FsbGVkIGEgX251bGwgbW9kZWxfLiBTaW5jZSB0aGUgdmFyaWFibGVzIGFyZSBzZXQgdG8gemVybywgdGhpcyBtb2RlbCBoYXMgbm8gdXNlZnVsIGluZm9ybWF0aW9uIHRvIGVzdGltYXRlIHRoZSBwcm9iYWJpbGl0eSwgYW5kIHRoZXJlZm9yZSBpdCBhc3NpZ25zIGVxdWFsIHByb2JhYmlsaXRpZXMgdG8gYWxsIGFsdGVybmF0aXZlcy4gVGhlIHZhbHVlIG9mIHRoZSBsaWtlbGlob29kIGZ1bmN0aW9uIGlzIGEgcmVsYXRpdmVseSBzbWFsbCAocG9zaXRpdmUpIG51bWJlciAocmVtZW1iZXIsIHRoZSBmdW5jdGlvbiBpcyBib3VuZGVkIGJldHdlZW4gemVybyBhbmQgb25lKS4NCg0KTm93LCB3ZSBjYW4gZXhwZXJpbWVudCB3aXRoIHRoZSBjb2VmZmljaWVudHMsIGJ5IGdpdmluZyB0aGVtIGRpZmZlcmVudCB2YWx1ZXMgYXMgZm9sbG93cyAoY2FsbCB0aGlzICJFeHBlcmltZW50IDIiKToNCmBgYHtyfQ0KbXUgPC0gMC41ICMgLTAuNQ0KYmV0YSA8LSAtMC41ICMgLTAuNQ0KDQpQMUFfMiA8LSAoZXhwKGJldGEgKiB0cyR4aUFbMV0pLyhleHAoYmV0YSAqIHRzJHhpQVsxXSkgKyBleHAobXUgKyBiZXRhICogdHMkeGlCWzFdKSkpDQpQMUJfMiA8LSAoZXhwKG11ICsgYmV0YSAqIHRzJHhpQlsxXSkvKGV4cChiZXRhICogdHMkeGlBWzFdKSArIGV4cChtdSArIGJldGEgKiB0cyR4aUJbMV0pKSkNClAyQV8yIDwtIChleHAoYmV0YSAqIHRzJHhpQVsyXSkvKGV4cChiZXRhICogdHMkeGlBWzJdKSArIGV4cChtdSArIGJldGEgKiB0cyR4aUJbMl0pKSkNClAyQl8yIDwtIChleHAobXUgKyBiZXRhICogdHMkeGlCWzJdKS8oZXhwKGJldGEgKiB0cyR4aUFbMl0pICsgZXhwKG11ICsgYmV0YSAqIHRzJHhpQlsyXSkpKQ0KUDNBXzIgPC0gKGV4cChiZXRhICogdHMkeGlBWzNdKS8oZXhwKGJldGEgKiB0cyR4aUFbM10pICsgZXhwKG11ICsgYmV0YSAqIHRzJHhpQlszXSkpKQ0KUDNCXzIgPC0gKGV4cChtdSArIGJldGEgKiB0cyR4aUJbM10pLyhleHAoYmV0YSAqIHRzJHhpQVszXSkgKyBleHAobXUgKyBiZXRhICogdHMkeGlCWzNdKSkpDQpQNEFfMiA8LSAoZXhwKGJldGEgKiB0cyR4aUFbNF0pLyhleHAoYmV0YSAqIHRzJHhpQVs0XSkgKyBleHAobXUgKyBiZXRhICogdHMkeGlCWzRdKSkpDQpQNEJfMiA8LSAoZXhwKG11ICsgYmV0YSAqIHRzJHhpQls0XSkvKGV4cChiZXRhICogdHMkeGlBWzRdKSArIGV4cChtdSArIGJldGEgKiB0cyR4aUJbNF0pKSkNClA1QV8yIDwtIChleHAoYmV0YSAqIHRzJHhpQVs1XSkvKGV4cChiZXRhICogdHMkeGlBWzVdKSArIGV4cChtdSArIGJldGEgKiB0cyR4aUJbNV0pKSkNClA1Ql8yIDwtIChleHAobXUgKyBiZXRhICogdHMkeGlCWzVdKS8oZXhwKGJldGEgKiB0cyR4aUFbNV0pICsgZXhwKG11ICsgYmV0YSAqIHRzJHhpQls1XSkpKQ0KUDZBXzIgPC0gKGV4cChiZXRhICogdHMkeGlBWzZdKS8oZXhwKGJldGEgKiB0cyR4aUFbNl0pICsgZXhwKG11ICsgYmV0YSAqIHRzJHhpQls2XSkpKQ0KUDZCXzIgPC0gKGV4cChtdSArIGJldGEgKiB0cyR4aUJbNl0pLyhleHAoYmV0YSAqIHRzJHhpQVs2XSkgKyBleHAobXUgKyBiZXRhICogdHMkeGlCWzZdKSkpDQogIA0KTCA8LSAgUDFBXzJedHMkeWlBWzFdICogUDFCXzJedHMkeWlCWzFdICogDQogIFAyQV8yXnRzJHlpQVsyXSAqIFAyQl8yXnRzJHlpQlsyXSAqIA0KICBQM0FfMl50cyR5aUFbM10gKiBQM0JfMl50cyR5aUJbM10gKiANCiAgUDRBXzJedHMkeWlBWzRdICogUDRCXzJedHMkeWlCWzRdICogDQogIFA1QV8yXnRzJHlpQVs1XSAqIFA1Ql8yXnRzJHlpQls1XSAqIA0KICBQNkFfMl50cyR5aUFbNl0gKiBQNkJfMl50cyR5aUJbNl0gDQoNCiMgQ3JlYXRlIGRhdGEgZnJhbWUgdG8gdGFidWxhdGUgcmVzdWx0czoNCmRmIDwtIGRhdGEuZnJhbWUoSW5kaXZpZHVhbCA9IGMoMSwgMiwgMywgNCwgNSwgNiksDQogICAgICAgICAgICAgICAgIENob2ljZSA9IGMoIkEiLCAiQSIsICJCIiwgIkEiLCAiQiIsICJCIiksDQogICAgICAgICAgICAgICAgIFBBID0gYyhQMUFfMiwgUDJBXzIsIFAzQV8yLCBQNEFfMiwgUDVBXzIsIFA2QV8yKSwNCiAgICAgICAgICAgICAgICAgUEIgPSBjKFAxQl8yLCBQMkJfMiwgUDNCXzIsIFA0Ql8yLCBQNUJfMiwgUDZCXzIpKQ0KDQprYWJsZShkZiwgImh0bWwiLCBkaWdpdHMgPSA0LCBhbGlnbiA9ICJjIikgJT4lDQogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIikpICU+JQ0KICBmb290bm90ZShnZW5lcmFsID0gcGFzdGUoIlRoZSB2YWx1ZSBvZiB0aGUgbGlrZWxpaG9vZCBmdW5jdGlvbiBpcyAiLCByb3VuZChMLCBkaWdpdHMgPSA0KSkpDQpgYGANCg0KTm90aWNlIGhvdyBjaGFuZ2luZyB0aGUgY29lZmZpY2llbnRzIGhhcyB0d28gZWZmZWN0cywgYXMgeW91IHdvdWxkIGV4cGVjdDogdGhlIHByb2JhYmlsaXRpZXMgY2hhbmdlIGFuZCB0aGUgdmFsdWUgb2YgdGhlIGxpa2VsaWhvb2QgZnVuY3Rpb24gY2hhbmdlcyB0b28uIEluc3BlY3QgdGhlIHByb2JhYmlsaXRpZXMgYW5kIHRoZSB2YWx1ZSBvZiB0aGUgbGlrZWxpaG9vZCBmdW5jdGlvbiB3aXRoIHRoZSBuZXcgY29lZmZpY2llbnRzLiBXaGF0IGRvIHlvdSBub3RpY2U/DQoNCklmIHlvdSBhcmUgd29ya2luZyB3aXRoIHRoZSBSIE5vdGVib29rLCBhdCB0aGlzIHBvaW50IHlvdSBjYW4gdHJ5IGNoYW5naW5nIHRoZSBjb2VmZmljaWVudHMuIENhbiB5b3UgaW1wcm92ZSB0aGUgdmFsdWUgb2YgdGhlIGxpa2VsaWhvb2QgZnVuY3Rpb24sIG9yIG1heWJlIGV2ZW4gbWFrZSBpdCB3b3JzZT8NCg0KVGhlIGxpa2VsaWhvb2QgZnVuY3Rpb24gY2FuIGJlIHBsb3R0ZWQgYXMgc2hvd24gYmVsb3cuIElmIHlvdSBob3ZlciBvdmVyIHRoZSBwbG90LCB5b3UgY2FuIHNlZSBob3cgdGhlIHZhbHVlIG9mIHRoZSBsaWtlbGlob29kIGNoYW5nZXMgYXMgYSBmdW5jdGlvbiBvZiAkXG11JCBhbmQgJFxiZXRhJDoNCmBgYHtyIGZpZy1saWtlbGlob29kLWZ1bmN0aW9uLCBlY2hvPUZBTFNFLCBmaWcuY2FwPSAiXFxsYWJlbHtmaWc6ZmlnLWxpa2VsaWhvb2QtZnVuY3Rpb259TGlrZWxpaG9vZCBmdW5jdGlvbiBmb3IgdG95IGRhdGFzZXQifQ0KIyBDcmVhdGUgYSBncmlkIHRvIHBsb3QgdGhlIGxpa2VsaWhvb2QgZnVuY3Rpb24NCm11ID0gc2VxKGZyb20gPSAtMSwgdG8gPSAxLCBieSA9IDAuMDUpDQpiZXRhID0gc2VxKGZyb20gPSAtMiwgdG8gPSAwLCBieSA9IDAuMDUpDQpjb2VmZnMgPC0gZXhwYW5kLmdyaWQobXUsIGJldGEpDQoNCiMgRGVmaW5lIHRoZSBsaWtlbGlob29kIGZ1bmN0aW9uDQpsa2ggPC0gZnVuY3Rpb24obXUgPSAwLCBiZXRhID0gMCl7DQogIHRzIDwtIGRhdGEuZnJhbWUoSW5kaXZpZHVhbCA9IGMoMSwgMiwgMywgNCwgNSwgNiksDQogICAgICAgICAgICAgICAgICAgICAgICAgQ2hvaWNlID0gYygiQSIsICJBIiwgIkIiLCAiQSIsICJCIiwgIkIiKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgeWlBID0gYygxLCAxLCAwLCAxLCAwLCAwKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICB5aUIgPSBjKDAsIDAsIDEsIDAsIDEsIDEpLA0KICAgICAgICAgICAgICAgICAgICAgICAgIHhpQSA9IGMoNSwgMiwgNSwgMSwgNCwgMyksDQogICAgICAgICAgICAgICAgICAgICAgICAgeGlCID0gYyg0LCA1LCAyLCA2LCAxLCA0KSkNCiAgDQogIFAxQSA8LSAoZXhwKGJldGEgKiB0cyR4aUFbMV0pLyhleHAoYmV0YSAqIHRzJHhpQVsxXSkgKyBleHAobXUgKyBiZXRhICogdHMkeGlCWzFdKSkpDQogIFAxQiA8LSAoZXhwKG11ICsgYmV0YSAqIHRzJHhpQlsxXSkvKGV4cChiZXRhICogdHMkeGlBWzFdKSArIGV4cChtdSArIGJldGEgKiB0cyR4aUJbMV0pKSkNCiAgUDJBIDwtIChleHAoYmV0YSAqIHRzJHhpQVsyXSkvKGV4cChiZXRhICogdHMkeGlBWzJdKSArIGV4cChtdSArIGJldGEgKiB0cyR4aUJbMl0pKSkNCiAgUDJCIDwtIChleHAobXUgKyBiZXRhICogdHMkeGlCWzJdKS8oZXhwKGJldGEgKiB0cyR4aUFbMl0pICsgZXhwKG11ICsgYmV0YSAqIHRzJHhpQlsyXSkpKQ0KICBQM0EgPC0gKGV4cChiZXRhICogdHMkeGlBWzNdKS8oZXhwKGJldGEgKiB0cyR4aUFbM10pICsgZXhwKG11ICsgYmV0YSAqIHRzJHhpQlszXSkpKQ0KICBQM0IgPC0gKGV4cChtdSArIGJldGEgKiB0cyR4aUJbM10pLyhleHAoYmV0YSAqIHRzJHhpQVszXSkgKyBleHAobXUgKyBiZXRhICogdHMkeGlCWzNdKSkpDQogIFA0QSA8LSAoZXhwKGJldGEgKiB0cyR4aUFbNF0pLyhleHAoYmV0YSAqIHRzJHhpQVs0XSkgKyBleHAobXUgKyBiZXRhICogdHMkeGlCWzRdKSkpDQogIFA0QiA8LSAoZXhwKG11ICsgYmV0YSAqIHRzJHhpQls0XSkvKGV4cChiZXRhICogdHMkeGlBWzRdKSArIGV4cChtdSArIGJldGEgKiB0cyR4aUJbNF0pKSkNCiAgUDVBIDwtIChleHAoYmV0YSAqIHRzJHhpQVs1XSkvKGV4cChiZXRhICogdHMkeGlBWzVdKSArIGV4cChtdSArIGJldGEgKiB0cyR4aUJbNV0pKSkNCiAgUDVCIDwtIChleHAobXUgKyBiZXRhICogdHMkeGlCWzVdKS8oZXhwKGJldGEgKiB0cyR4aUFbNV0pICsgZXhwKG11ICsgYmV0YSAqIHRzJHhpQls1XSkpKQ0KICBQNkEgPC0gKGV4cChiZXRhICogdHMkeGlBWzZdKS8oZXhwKGJldGEgKiB0cyR4aUFbNl0pICsgZXhwKG11ICsgYmV0YSAqIHRzJHhpQls2XSkpKQ0KICBQNkIgPC0gKGV4cChtdSArIGJldGEgKiB0cyR4aUJbNl0pLyhleHAoYmV0YSAqIHRzJHhpQVs2XSkgKyBleHAobXUgKyBiZXRhICogdHMkeGlCWzZdKSkpDQogIA0KICBQMUFedHMkeWlBWzFdICogUDFCXnRzJHlpQlsxXSAqIA0KICBQMkFedHMkeWlBWzJdICogUDJCXnRzJHlpQlsyXSAqIA0KICBQM0FedHMkeWlBWzNdICogUDNCXnRzJHlpQlszXSAqIA0KICBQNEFedHMkeWlBWzRdICogUDRCXnRzJHlpQls0XSAqIA0KICBQNUFedHMkeWlBWzVdICogUDVCXnRzJHlpQls1XSAqIA0KICBQNkFedHMkeWlBWzZdICogUDZCXnRzJHlpQls2XSANCn0NCg0KIyBFdmFsdWF0ZSB0aGUgbGlrZWxpaG9vZCBmdW5jdGlvbiBvbiB0aGUgZ3JpZA0KTCA8LSBsa2gobXUgPSBjb2VmZnMkVmFyMSwgYmV0YSA9IGNvZWZmcyRWYXIyKQ0KDQpMIDwtIGRhdGEuZnJhbWUobXUgPSBjb2VmZnMkVmFyMSwgYmV0YSA9IGNvZWZmcyRWYXIyLCBMKQ0KTCA8LSB4dGFicyhMIH4gYmV0YSArIG11LCBMKQ0KDQpwbG90X2x5KHogPSB+TCwgeCA9IH5tdSwgeSA9IH5iZXRhKSAlPiUgDQogIGFkZF9zdXJmYWNlKCkgJT4lDQogIGxheW91dChzY2VuZSA9IGxpc3QoDQogICAgICB4YXhpcyA9IGxpc3QodGl0bGUgPSAieC1heGlzIChtdSkiKSwNCiAgICAgIHlheGlzID0gbGlzdCh0aXRsZSA9ICJ5LWF4aXMgKGJldGEpIiksDQogICAgICB6YXhpcyA9IGxpc3QodGl0bGUgPSAiJHokLWF4aXMgKEwpIikNCiAgICApKQ0KDQpgYGANCg0KRnJvbSBGaWd1cmUgXEByZWYoZmlnOmZpZy1ldmktZGlzdHJpYnV0aW9uKSB3ZSBjYW4gc2VlIHRoYXQgdGhlIGFwcHJveGltYXRlIHZhbHVlcyBvZiB0aGUgY29lZmZpY2llbnRzIHRoYXQgbWF4aW1pemUgdGhlIGxpa2VsaWhvb2QgZnVuY3Rpb24gYXJlICRcbXU9MC4xMCQgYW5kICRcYmV0YT0tMC42NSQuIElmIHdlIHVzZSB0aGVzZSBjb2VmZmljaWVudHMgdG8gY2FsY3VsYXRlIHRoZSBsb2dpdCBwcm9iYWJpbGl0aWVzLCB3ZSBjYW4gY29tcGFyZSB0byB0aGUgcHJvYmFiaWxpdGllcyBvZiBFeHBlcmltZW50cyAxIGFuZCAyOg0KYGBge3J9DQojIEFwcHJveGltYXRlIHZhbHVlcyB0aGF0IG1heGltaXplIHRoZSBsaWtlbGlob29kIGZ1bmN0aW9uLg0KbXUgPC0gMC4xMA0KYmV0YSA8LSAtMC42NQ0KDQpQMUFfMyA8LSAoZXhwKGJldGEgKiB0cyR4aUFbMV0pLyhleHAoYmV0YSAqIHRzJHhpQVsxXSkgKyBleHAobXUgKyBiZXRhICogdHMkeGlCWzFdKSkpDQpQMUJfMyA8LSAoZXhwKG11ICsgYmV0YSAqIHRzJHhpQlsxXSkvKGV4cChiZXRhICogdHMkeGlBWzFdKSArIGV4cChtdSArIGJldGEgKiB0cyR4aUJbMV0pKSkNClAyQV8zIDwtIChleHAoYmV0YSAqIHRzJHhpQVsyXSkvKGV4cChiZXRhICogdHMkeGlBWzJdKSArIGV4cChtdSArIGJldGEgKiB0cyR4aUJbMl0pKSkNClAyQl8zIDwtIChleHAobXUgKyBiZXRhICogdHMkeGlCWzJdKS8oZXhwKGJldGEgKiB0cyR4aUFbMl0pICsgZXhwKG11ICsgYmV0YSAqIHRzJHhpQlsyXSkpKQ0KUDNBXzMgPC0gKGV4cChiZXRhICogdHMkeGlBWzNdKS8oZXhwKGJldGEgKiB0cyR4aUFbM10pICsgZXhwKG11ICsgYmV0YSAqIHRzJHhpQlszXSkpKQ0KUDNCXzMgPC0gKGV4cChtdSArIGJldGEgKiB0cyR4aUJbM10pLyhleHAoYmV0YSAqIHRzJHhpQVszXSkgKyBleHAobXUgKyBiZXRhICogdHMkeGlCWzNdKSkpDQpQNEFfMyA8LSAoZXhwKGJldGEgKiB0cyR4aUFbNF0pLyhleHAoYmV0YSAqIHRzJHhpQVs0XSkgKyBleHAobXUgKyBiZXRhICogdHMkeGlCWzRdKSkpDQpQNEJfMyA8LSAoZXhwKG11ICsgYmV0YSAqIHRzJHhpQls0XSkvKGV4cChiZXRhICogdHMkeGlBWzRdKSArIGV4cChtdSArIGJldGEgKiB0cyR4aUJbNF0pKSkNClA1QV8zIDwtIChleHAoYmV0YSAqIHRzJHhpQVs1XSkvKGV4cChiZXRhICogdHMkeGlBWzVdKSArIGV4cChtdSArIGJldGEgKiB0cyR4aUJbNV0pKSkNClA1Ql8zIDwtIChleHAobXUgKyBiZXRhICogdHMkeGlCWzVdKS8oZXhwKGJldGEgKiB0cyR4aUFbNV0pICsgZXhwKG11ICsgYmV0YSAqIHRzJHhpQls1XSkpKQ0KUDZBXzMgPC0gKGV4cChiZXRhICogdHMkeGlBWzZdKS8oZXhwKGJldGEgKiB0cyR4aUFbNl0pICsgZXhwKG11ICsgYmV0YSAqIHRzJHhpQls2XSkpKQ0KUDZCXzMgPC0gKGV4cChtdSArIGJldGEgKiB0cyR4aUJbNl0pLyhleHAoYmV0YSAqIHRzJHhpQVs2XSkgKyBleHAobXUgKyBiZXRhICogdHMkeGlCWzZdKSkpDQogIA0KTCA8LSAgUDFBXzNedHMkeWlBWzFdICogUDFCXzNedHMkeWlCWzFdICogDQogIFAyQV8zXnRzJHlpQVsyXSAqIFAyQl8zXnRzJHlpQlsyXSAqIA0KICBQM0FfM150cyR5aUFbM10gKiBQM0JfM150cyR5aUJbM10gKiANCiAgUDRBXzNedHMkeWlBWzRdICogUDRCXzNedHMkeWlCWzRdICogDQogIFA1QV8zXnRzJHlpQVs1XSAqIFA1Ql8zXnRzJHlpQls1XSAqIA0KICBQNkFfM150cyR5aUFbNl0gKiBQNkJfM150cyR5aUJbNl0gDQoNCiMgQ3JlYXRlIGRhdGEgZnJhbWUgdG8gdGFidWxhdGUgcmVzdWx0czoNCmRmIDwtIGRhdGEuZnJhbWUoSW5kaXZpZHVhbCA9IGMoMSwgMiwgMywgNCwgNSwgNiksDQogICAgICAgICAgICAgICAgIENob2ljZSA9IGMoIkEiLCAiQSIsICJCIiwgIkEiLCAiQiIsICJCIiksDQogICAgICAgICAgICAgICAgIFBBXzEgPSBjKFAxQV8xLCBQMkFfMSwgUDNBXzEsIFA0QV8xLCBQNUFfMSwgUDZBXzEpLA0KICAgICAgICAgICAgICAgICBQQl8xID0gYyhQMUJfMSwgUDJCXzEsIFAzQl8xLCBQNEJfMSwgUDVCXzEsIFA2Ql8xKSwNCiAgICAgICAgICAgICAgICAgUEFfMSA9IGMoUDFBXzIsIFAyQV8yLCBQM0FfMiwgUDRBXzIsIFA1QV8yLCBQNkFfMiksDQogICAgICAgICAgICAgICAgIFBCXzEgPSBjKFAxQl8yLCBQMkJfMiwgUDNCXzIsIFA0Ql8yLCBQNUJfMiwgUDZCXzIpLA0KICAgICAgICAgICAgICAgICBQQV8xID0gYyhQMUFfMywgUDJBXzMsIFAzQV8zLCBQNEFfMywgUDVBXzMsIFA2QV8zKSwNCiAgICAgICAgICAgICAgICAgUEJfMSA9IGMoUDFCXzMsIFAyQl8zLCBQM0JfMywgUDRCXzMsIFA1Ql8zLCBQNkJfMykpDQoNCmthYmxlKGRmLCAiaHRtbCIsIGRpZ2l0cyA9IDQsIA0KICAgICAgY29sLm5hbWVzID0gYygiSW5kaXZpZHVhbCIsICJDaG9pY2UiLCAiUEEiLCAiUEIiLCAiUEEiLCAiUEIiLCAiUEEiLCAiUEIiKSwNCiAgICAgIGFsaWduID0gImMiKSAlPiUNCiAga2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiaG92ZXIiKSkgJT4lDQogIGFkZF9oZWFkZXJfYWJvdmUoYygiICIgPSAxLCAiICIgPSAxLCAiRXhwZXJpbWVudCAxIiA9IDIsICJFeHBlcmltZW50IDIiID0gMiwgIkFwcHJveCBNYXggTGlrZWxpaG9vZCIgPSAyKSkNCmBgYA0KDQpNYXhpbWl6aW5nIHRoZSBsaWtlbGlob29kIGlzIGEgdXNlZnVsIGNyaXRlcmlvbiB0byBlc3RpbWF0ZSB0aGUgY29lZmZpY2llbnRzIG9mIHRoZSBtb2RlbHMsIHNpbmNlIHRoaXMgY3JpdGVyaW9uIHByb3ZpZGVzIHRoZSBvcHRpbWFsIHByb2JhYmlsaXRpZXMgb2YgdGhlIHJpZ2h0IGFsdGVybmF0aXZlIGJlaW5nIGNob3Nlbi4gTWluZCB5b3UsIHRoaXMgZG9lcyBub3QgbmVjZXNzYXJpbHkgbWVhbiB0aGF0IHRob3NlIHByb2JhYmlsaXRpZXMgd2lsbCBiZSBoaWdoIC0gaG93ZXZlciwgd2UgY2FuIGJlIGNlcnRhaW4gdGhhdCB0aGV5IHdpbGwgYmUgdGhlIGJlc3QgZm9yIHRoZSBtb2RlbCB1bmRlciBjb25zaWRlcmF0aW9uIGZvciB0aGUgc2FtcGxlIGdpdmVuLg0KDQpJbiB0aGlzIHRveSBleGFtcGxlIHdlICJzb2x2ZWQiIHRoZSBwcm9ibGVtIG9mIG1heGltaXppbmcgdGhlIGxpa2VsaWhvb2QgYnkgaGFuZC4gVGhpcyBpcyByYXRoZXIgZGlmZmljdWx0LCB1bmZlYXNpYmxlIGV2ZW4sIGluIG1vc3QgYXBwbGllZCBzaXR1YXRpb25zIHdpdGggbGFyZ2Ugc2FtcGxlcyBhbmQvb3IgbW9yZSB0aGFuIG9uZSB2YXJpYWJsZS4gRm9ydHVuYXRlbHksIHRoZXJlIGFyZSBhIG51bWJlciBvZiBudW1lcmljYWwgYWxnb3JpdGhtcyB0aGF0IGNhbiBiZSB1c2VkIHRvIG1heGltaXplIHRoZSBsaWtlbGlob29kLiBXZSB3aWxsIG5vdCBkaXNjdXNzIHRoaXMgaW4gZGV0YWlsLCBidXQgaW50ZXJlc3RlZCByZWFkZXJzIGNhbiBjb25zdWx0IFRyYWluIFstQFRyYWluMjAwOWRpc2NyZXRlOyBTZWN0aW9uIDMuN10gZm9yIGRldGFpbHMuIFRoZSBgbWxvZ2l0YCBwYWNrYWdlIGltcG9ydHMgdGhlIHBhY2thZ2UgYG1heExpa2AgW0BIZW5uaW5nc2VuMjAxMW1heGxpa10sIHdoaWNoIGltcGxlbWVudHMgY2Fub25pY2FsIGFsZ29yaXRobXMgaW5jbHVkaW5nIFtOZXd0b24tUmFwaHNvbl0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvTmV3dG9uJTI3c19tZXRob2QpLCB0aGUgQmVybmR04oCTSGFsbOKAk0hhbGzigJNIYXVzbWFuIChvciBbQkhISF0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvQmVybmR0LUhhbGwtSGFsbC1IYXVzbWFuX2FsZ29yaXRobSkpLCBhbmQgdGhlIEJyb3lkZW7igJNGbGV0Y2hlcuKAk0dvbGRmYXJi4oCTU2hhbm5vIChvciBbQkZHU10oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvQnJveWRlbi1GbGV0Y2hlci1Hb2xkZmFyYi1TaGFubm9fYWxnb3JpdGhtKSkgYWxnb3JpdGhtLg0KDQpJbiBwcmFjdGljZSwgdGhlIGFsZ29yaXRobXMgYWJvdmUgbWF4aW1pemUgbm90IHRoZSBsaWtlbGlob29kIGZ1bmN0aW9uLCBidXQgYSB0cmFuc2Zvcm1hdGlvbiB0aGVyZW9mLCBjYWxsZWQgdGhlIF9sb2ctbGlrZWxpaG9vZF8sIHdoaWNoIGlzIG9idGFpbmVkIGJ5IHRha2luZyB0aGUgbmF0dXJhbCBsb2dhcml0aG0gb2YgdGhlIGZ1bmN0aW9uLCB0byBnaXZlOg0KDQokJA0KbCA9IFxzdW1fe2k9bn1eTlxzdW1fe2o9MX1eSiB5X3tpan1sb2coUF97aWp9KQ0KJCQNClNpbmNlIHRoZSBsaWtlbGlob29kIGZ1bmN0aW9uIGlzIGJvdW5kIGJldHdlZW4gemVybyBhbmQgb25lLCB0aGUgbG9nLWxpa2VsaWhvb2QgaXMgYm91bmQgYmV0d2VlbiBtaW51cyBpbmZpbml0eSBhbmQgemVyby4gVGhlIHZhbHVlIG9mIHRoZSBtYXhpbWl6ZWQgbG9nLWxpa2VsaWhvb2QgZnVuY3Rpb24gcHJvdmlkZXMgYSB1c2VmdWwgZGlhZ25vc3RpYyB0byBjb21wYXJlIG1vZGVscywgc2luY2UgaGlnaGVyIHZhbHVlcyBhcmUgaW5kaWNhdGl2ZSBvZiBhIGJldHRlciBtb2RlbC4gU2V2ZXJhbCBzdGF0aXN0aWNhbCB0ZXN0cyAoc3VjaCBhcyB0aGUgbGlrZWxpaG9vZCByYXRpbykgY2FuIGJlIHVzZWQgdG8gdGVzdCB0aGUgaHlwb3RoZXNpcyB0aGF0IGEgbW9kZWwgaXMgYSBzaWduaWZpY2FudCBpbXByb3ZlbWVudCBvdmVyIG90aGVyLCBhbmQgYXJlIHRodXMgdXNlZnVsIGZvciBtb2RlbCBzZWxlY3Rpb24gcHVycG9zZXMuIEhvd2V2ZXIsIGJlZm9yZSBkaXNjdXNzaW5nIG1vZGVsIGRpYWdub3N0aWNzLCB3ZSB3aWxsIHNlZSBob3cgbXVsdGlub21pYWwgbG9naXQgbW9kZWxzIGFyZSBlc3RpbWF0ZWQgdXNpbmcgYG1sb2dpdGAuDQoNCiMjIEV4YW1wbGU6IEEgbG9naXQgbW9kZWwgb2YgbW9kZSBjaG9pY2UNCg0KQ29taW5nIGJhY2sgdG8gdGhlIHRyYW5zcG9ydGF0aW9uIG1vZGUgY2hvaWNlIGRhdGFzZXQsIHdlIGhhZCBhbHJlYWR5IGRlZmluZWQgc29tZSBmb3JtdWxhcyAoaS5lLiwgdXRpbGl0eSBmdW5jdGlvbnMpIHRoYXQgd2UgY2FuIHVzZSB0byBlc3RpbWF0ZSBhIG1vZGVsLg0KDQpUaGUgZnVuY3Rpb24gdG8gZXN0aW1hdGUgYSBtb2RlbCBpcyBgbWxvZ2l0KClgLiBUaGlzIGZ1bmN0aW9uIHJlcXVpcmVzIGF0IGxlYXN0IHR3byBhcmd1bWVudHM6IGFuIGBtRm9ybXVsYWAgb2JqZWN0IGFuZCBhIGRhdGFzZXQuIFdlIGNhbiB2ZXJpZnkgdGhhdCB0aGUgZm9ybXVsYXMgd2UgY3JlYXRlZCBhYm92ZSBhcmUgb2YgdGhpcyBjbGFzczoNCmBgYHtyfQ0KY2xhc3MoZjEpDQpjbGFzcyhmMikNCmNsYXNzKGYzKQ0KYGBgDQoNClRoZSB2YWx1ZSAob3V0cHV0KSBvZiB0aGUgZnVuY3Rpb24gY2FuIGJlIG5hbWVkIGFuZCBzYXZlZCB0byBhbiBvYmplY3QgZm9yIGZ1cnRoZXIgYW5hbHlzaXMgb3IgZm9yIGZ1cnRoZXIgcHJvY2Vzc2luZywgcG9zdC1lc3RpbWF0aW9uLiBCZWdpbiBieSBlc3RpbWF0aW5nIGEgbW9kZWwgdXNpbmcgdGhlIHNpbXBsZXN0IG9mIG91ciBmb3JtdWxhczoNCmBgYHtyfQ0KbW9kZWwxIDwtIG1sb2dpdChmMSwgbWNfY29tbXV0ZV9sb25nKQ0Kc3VtbWFyeShtb2RlbDEpDQpgYGANCg0KVGhlIG91dHB1dCBvZiB0aGUgZnVuY3Rpb24gaW5jbHVkZXMgdGhlIGVzdGltYXRlZCBmcmVxdWVuY2llcyBvZiBhbHRlcm5hdGl2ZXMgaW4gYWRkaXRpb24gdG8gaW5mb3JtYXRpb24gYWJvdXQgdGhlIG9wdGltaXphdGlvbiBwcm9jZWR1cmUuIEZvciBpbnN0YW5jZSwgdGhlIG1lc3NhZ2UgInN1Y2Nlc3NpdmUgZnVuY3Rpb24gdmFsdWVzIHdpdGhpbiB0b2xlcmFuY2UgbGltaXRzIiBpbmRpY2F0ZXMgdGhhdCB0aGUgYWxnb3JpdGhtIGNvbnZlcmdlZCBub3JtYWxseS4gDQoNClRoZSBvdXRwdXQgYWxzbyByZXBvcnRzIHRoZSBlc3RpbWF0ZWQgdmFsdWVzIG9mIHRoZSBjb2VmZmljaWVudHMsIGFsb25nIHdpdGggc3RhbmRhcmQgZXJyb3JzLCAkeiQtdmFsdWVzLCBhbmQgJHAkLXZhbGx1ZXMuIFRoZSBudWxsIGh5cG90aGVzaXMgYXNzb2NpYXRlZCB3aXRoIHRoZSBjb2VmZmljaWVudHMgaXMgdGhhdCB0aGV5IGFyZSB6ZXJvLiBBbiBhbmFseXN0IGNhbiByZWplY3QgdGhlIG51bGwgaHlwb3RoZXNpcyBpbiBhbnkgY2FzZSwgYnV0IHNtYWxsICRwJC12YWxsdWVzIGluZGljYXRlIGEgbG93IHByb2JhYmlsaXR5IHRoYXQgdGhlIGNvZWZmaWNpZW50IGlzIHplcm8gLSBhbmQgdGhlcmVmb3JlIGluY3JlYXNlIHRoZSBjb25maWRlbmNlIHRoYXQgYnkgcmVqZWN0aW5nIHRoZSBudWxsIHRoZSBhbmFseXN0IGlzIG5vdCBtaXN0YWtlbmx5IHJlamVjdGluZyBhIHRydWUgemVyby4gSW4gdGhlIHByZXNlbnQgY2FzZSwgd2l0aCAkcCQtdmFsbHVlcyBzbWFsbGVyIHRoYW4gMC4wMDAxLCB0aGUgbnVsbCBoeXBvdGhlc2lzIGNhbiBiZSBjb21mb3J0YWJseSByZWplY3RlZCBmb3IgZXZlcnkgY29lZmZpY2llbnQuIFRoZSBzbWFsbCAkcCQtdmFsbHVlcyBtZWFuIHRoYXQgaXQgaXMgaGlnaGx5IHVubGlrZWx5IHRoYXQgdGhlIGNvZWZmaWNpZW50cyBhcmUgemVyby4NCg0KVGhpcyBzaW1wbGUgbW9kZWwgaW5jbHVkZXMgdGhyZWUgYWx0ZXJuYXRpdmUtc3BlY2lmaWMgY29uc3RhbnRzIGFuZCBvbmUgYWx0ZXJuYXRpdmUtc3BlY2lmaWMgdmFyaWFibGUgd2l0aCBhIGdlbmVyaWMgY29lZmZpY2llbnQuIFRoZSBzaWducyBvZiB0aGUgY29lZmZpY2llbnRzIGFyZSBpbmZvcm1hdGl2ZS4gU2luY2UgdGhlIHJlZmVyZW5jZSBtb2RlIGlzICJDYXIiLCB0aGUgcG9zaXRpdmUgdmFsdWVzIG9mIHRoZSBjb25zdGFudHMgaW5kaWNhdGUgdGhhdCwgb3RoZXIgdGhpbmdzIGJlaW5nIGVxdWFsLCBjYXIgaXMgdGhlIGxlYXN0IHByZWZlcnJlZCBtb2RlLCBmb2xsb3dlZCBieSBjeWNsaW5nLCBIU1IsIGFuZCB0aGVuIHdhbGsgKHdoaWNoIGdpdmVzIHRoZSBoaWdoZXN0IHV0aWxpdHkgYXQgYSBjb25zdGFudCB2YWx1ZSBvZiB0aW1lKS4gVGhpcyBpcyB2ZXJpZmllZCBmcm9tIHRoZSBlc3RpbWF0ZWQgZnJlcXVlbmNpZXMgb2YgdGhlIG1vZGVzLCB3aGVyZSB3ZSBzZWUgdGhhdCAiV2FsayIgaXMgdGhlIG1vZGUgb2YgY2hvaWNlIG9mIDUxLjclIG9mIHJlc3BvbmRlbnRzIGluIHRoZSBzYW1wbGUuDQoNClRoZSBuZWdhdGl2ZSBjb2VmZmljaWVudCBmb3IgdGltZSBpbmRpY2F0ZXMgdGhhdCB0aW1lIGlzIGEgImNvc3QiLCBpbiBvdGhlciB3b3JkcywgdGhlIHV0aWxpdHkgb2YgdHJhdmVsbGluZyB0ZW5kcyB0byBkZWNsaW5lIHdpdGggaW5jcmVhc2luZyB0cmF2ZWwgdGltZXMuIFRoaXMgaW5kaWNhdGVzIHRoYXQgc2xvd2VyIG1vZGVzIHdpbGwgdGVuZCB0byBoYXZlIGxvd2VyIHV0aWxpdGllcy4NCg0KRmluYWxseSwgdGhlIG1heGltaXplZCB2YWx1ZSBvZiB0aGUgbG9nLWxpa2VsaWhvb2QgZnVuY3Rpb24gaXMgcmVwb3J0ZWQsIGFsb25nIHdpdGggdHdvIGRpYWdub3N0aWNzLCBNY0ZhZGRlbiBSXjIgKGluIHJlYWxpdHkgJFxyaG9eMiQpIGFuZCBhIGxpa2VsaWhvb2QgcmF0aW8gdGVzdC4gV2Ugd2lsbCBjb21lIGJhY2sgdG8gdGhlc2UgZGlhZ25vc3RpY3MgYmVsb3csIGJ1dCBmaXJzdCwgd2Ugd2lsbCBlc3RpbWF0ZSBhIG5ldyBtb2RlbCB1c2luZyB0aGUgc2Vjb25kIGZvcm11bGEuDQpgYGB7cn0NCm1vZGVsMiA8LSBtbG9naXQoZjIsIG1jX2NvbW11dGVfbG9uZykNCnN1bW1hcnkobW9kZWwyKQ0KYGBgDQoNCk5vdyB0aGVyZSBpcyBhbiBpbmRpdmlkdWFsLXNwZWNpZmljIHZhcmlhYmxlIGluIHRoZSBtb2RlbCAoaS5lLiwgc2lkZXdhbGsgZGVuc2l0eSkuIE9ubHkgb25lIG9mIHRob3NlIGNvZWZmaWNpZW50cyBpcyBzaWduaWZpY2FudCBhdCBhIGNvbnZlbnRpb25hbCBsZXZlbCBvZiBzaWduaWZpY2FuY2UgKGkuZS4sICRwPDAuMDUkKSwgYW5kIHRoZSB2YWx1ZXMgaXMgcG9zaXRpdmUuIFNpbmNlIHRoZSByZWZlcmVuY2UgaXMgIkNhciIsIGEgcG9zaXRpdmUgdmFsdWUgaW5kaWNhdGVzIHRoYXQgaGlnaGVyIHNpZGV3YWxrIGRlbnNpdHkgY2F1c2VzIHRoZSB1dGlsaXR5IG9mIHdhbGtpbmcgdG8gaW5jcmVhc2Ugd2l0aCByZXNwZWN0IHRvIHRoZSB1dGlsaXR5IG9mIHVzaW5nIGEgY2FyLiBUaGUgc2FtZSBpcyBub3QgdHJ1ZSBmb3IgSFNSIGFuZCAiQ3ljbGUiLCB3aG9zZSBjb2VmZmljaWVudHMgZm9yIHRoaXMgYXR0cmlidXRlIGFyZSBub3Qgc2lnbmlmaWNhbnRseSBkaWZmZXJlbnQgZnJvbSAiQ2FyIi4NCg0KTm90ZSB0aGF0IGl0IGlzIHBvc3NpYmxlIHRvIHNlbGVjdCB0aGUgcmVmZXJlbmNlIGxldmVsIGZvciB0aGUgdXRpbGl0aWVzIHdoZW4gZXN0aW1hdGluZyB0aGUgbW9kZWwuIEZvciBleGFtcGxlLCBiZWxvdyB3ZSByZS1lc3RpbWF0ZSB0aGUgcHJlY2VkaW5nIG1vZGVsLCBidXQgbm93IHVzaW5nIHRoZSB1dGlsaXR5IG9mIFdhbGsgYXMgdGhlIHJlZmVyZW5jZToNCmBgYHtyfQ0KbW9kZWwyIDwtIG1sb2dpdChmMiwgbWNfY29tbXV0ZV9sb25nLCByZWZsZXZlbCA9ICJXYWxrIikNCnN1bW1hcnkobW9kZWwyKQ0KYGBgDQoNCk5vdGljZSB0aGF0IG5vdyB0d28gc2lkZXdhbGsgY29lZmZpY2llbnQgYXJlIHNpZ25pZmljYW50ISBXaGlsZSBzaWRld2FsayBkZW5zaXR5IGRvZXMgbm90IHNpZ25pZmljYW50bHkgY2hhbmdlIHRoZSB1dGlsaXR5IG9mIGN5Y2xpbmcgd2l0aCByZXNwZWN0IHRvIHdhbGtpbmcsIGxpdmluZyBpbiBhIHBsYWNlIHdpdGggaGlnaCBzaWRld2FsayBkZW5zaXR5IHJlZHVjZXMgdGhlIHV0aWxpdHkgb2YgIkNhciIgYW5kICJIU1IiIF93aXRoIHJlc3BlY3QgdG8gd2Fsa2luZ18uDQoNClRoZSB2YWx1ZSBvZiB0aGUgbWF4aW1pemVkIGxvZy1saWtlbGlob29kIGFuZCBvdGhlciBtb2RlbCBkaWFnbm9zdGljcyBhcmUgaWRlbnRpY2FsIGlycmVzcGVjdGl2ZSBvZiB3aGljaCBtb2RlIGlzIHNlbGVjdGVkIGFzIGEgdXRpbGl0eS4gSW4gZXNzZW5jZSwgdGhlIG1vZGVscyBhcmUgdGhlIHNhbWUsIGJ1dCB0aGV5IHByb3ZpZGUgYSBkaWZmZXJlbnQgcGVyc3BlY3RpdmUgb24gaG93IHNvbWUgY29lZmZpY2llbnRzIHJlbGF0ZSB0byBlYWNoIG90aGVyIGFjcm9zcyBhbHRlcm5hdGl2ZXMuDQoNCldlIGNhbiB2aXN1YWxseSBleHBsb3JlIGhvdyB0aGUgcHJvYmFiaWxpdHkgb2YgY2hvb3NpbmcgZGlmZmVyZW50IG1vZGVzIHZhcmllcyB3aXRoIHNpZGV3YWxrIGRlbnNpdHkuIFRvIGRvIHRoaXMgd2Ugd2lsbCBmaXJzdCBzdW1tYXJpemUgdGhlIHNpZGV3YWxrIGRlbnNpdHkgdmFyaWFibGU6DQpgYGB7cn0NCnN1bW1hcnkobWNfY29tbXV0ZV9sb25nJHNpZGV3YWxrX2RlbnNpdHkpDQpgYGANCg0KQ29weSB0aGUgZGF0YWZyYW1lIHVzZWQgdG8gZXN0aW1hdGUgdGhlIG1vZGVsLCBidXQgb25seSBlbm91Z2ggY29sdW1ucyB0byBleHBsb3JlIHNpZGV3YWxrIGRlbnNpdGllcyBpbiB0aGUgcmFuZ2UgYmV0d2VlbiAwIGFuZCA2MCAsIGluIGludGVydmFscyBvZiA1LiBUaGVyZWZvcmUgd2UgbmVlZCB0byBjb3B5IDYwIHJvd3MgZnJvbSB0aGUgbG9uZyB0YWJsZSAodGhpcnRlZW4gbGV2ZWxzIG9mIHNpZGV3YWxrIGRlbnNpdHkgdGltZXMgZm91ciBhbHRlcm5hdGl2ZXMpOg0KYGBge3J9DQptY19jb21tdXRlX3ByZWRpY3QgPC0gbWNfY29tbXV0ZV9sb25nWzE6NTIsXQ0KYGBgDQoNClJlcGxhY2UgdGhlIHRpbWUgdmFyaWFibGUgdXNpbmcgdmFsdWVzIGZvciB0aW1lcyAxIHRvIDIwLiBTaW5jZSBlYWNoIGFsdGVybmF0aXZlIGlzIGEgcm93LCB3ZSBuZWVkIHRvIGNyZWF0ZSBhIHNlcXVlbmNlIG9mIHJlcGxpY2F0ZWQgdmFsdWVzIGFzIGZvbGxvd3M6DQpgYGB7cn0NCm1jX2NvbW11dGVfcHJlZGljdCRzaWRld2Fsa19kZW5zaXR5IDwtIHJlcChzZXEoMCwgNjAsIDUpLCBlYWNoID0gNCkNCmBgYA0KDQpXZSBjYW4gZXhhbWluZSB0aGUgcmVzdWx0cyBvZiBzaW11bGF0aW5nIHRoZSBzaWRld2FsayBkZW5zaXR5Og0KYGBge3J9DQpzZWxlY3QobWNfY29tbXV0ZV9wcmVkaWN0LCBzaWRld2Fsa19kZW5zaXR5KSAlPiUgaGVhZCg4KQ0KYGBgDQoNClRoZSBwcmVkaWN0aW9uIGRhdGFmcmFtZSBub3cgaW5jbHVkZXMgdGhlIHJhbmdlIG9mIHNpZGV3YWxrIGRlbnNpdGllcyB0aGF0IHdlIGFyZSBpbnRlcmVzdGVkIGluLg0KDQpUaGUgc2V0dXAgZm9yIHRoZSBzaW11bGF0aW9uIGVzc2VudGlhbGx5IGFtb3VudHMgdG86IHdoYXQgaXMgdGhlIHByb2JhYmlsaXR5IG9mIGNob29zaW5nIGVhY2ggbW9kZSBmb3IgYSB0cmlwIHRoYXQgd291bGQgdGFrZSAxIG1pbj8gRm9yIHRoZSBzaW11bGF0aW9uLCB3ZSBhbHNvIG5lZWQgdG8gc2V0IHRoZSB2YWx1ZXMgb2Ygb3RoZXIgdmFyaWFibGVzIG9mIGludGVyZXN0LiBTaW5jZSB0aGUgbW9kZWwgaGFzIHRoZSB2YXJpYWJsZSBgdGltZWAgd2UgbmVlZCB0byBhbHNvIHNldCBpdCB0byBhIGRlc2lyZWQgdmFsdWUsIGZvciBpbnN0YW5jZSwgdGhlIG1lZGlhbiB0cmlwIGR1cmF0aW9uIChwYXlpbmcgYXR0ZW50aW9uIHRvIHJlbW92ZSB0aGUgbWlzc2luZyB2YWx1ZXMpOg0KYGBge3J9DQptZWRpYW4obWNfY29tbXV0ZV9sb25nJHRpbWUsIG5hLnJtID0gVFJVRSkNCmBgYA0KDQpUaGUgbWVkaWFuIHRyaXAgbGVuZ3RoIGlzIDEwIG1pbnV0ZXMsIHNvIHdlIHJlcGxhY2UgdGhlIGN1cnJlbnQgdmFsdWVzIGluIHRoZSBwcmVkaWN0aW9uIGRhdGFmcmFtZToNCmBgYHtyfQ0KbWNfY29tbXV0ZV9wcmVkaWN0JHRpbWUgPC0gMTANCmBgYA0KDQpFeGFtaW5lIHRoZSByZWxldmFudCB2YXJpYWJsZXMgb2YgdGhlIHByZWRpY3Rpb24gZGF0YWZyYW1lOg0KYGBge3J9DQptY19jb21tdXRlX3ByZWRpY3QgJT4lIHNlbGVjdCh0aW1lLCBzaWRld2Fsa19kZW5zaXR5KSAlPiUgc3VtbWFyeSgpDQpgYGANCg0KTmV4dCwgcHJlZGljdCB0aGUgcHJvYmFiaWxpdGllcyB1c2luZyB0aGUgYHByZWRpY3QoKWAgZnVuY3Rpb24gYW5kIGBtb2RlbDJgOg0KYGBge3J9DQpwcm9icyA8LSBwcmVkaWN0KG1vZGVsMiwgbmV3ZGF0YSA9IG1jX2NvbW11dGVfcHJlZGljdCkNCmBgYA0KDQpUaGUgdmFsdWUgKG91dHB1dCkgb2YgYHByZWRpY3RgIGlzIGEgMjAtYnktNCBtYXRyaXggdGhhdCBjb250YWlucyB0aGUgcHJvYmFiaWxpdHkgZm9yIHR3ZW50eSB0cmF2ZWwgdGltZSB2YWx1ZXMgKGkuZS4sICQxLCAyLCAzLFxjZG90cywgMjAkKSwgYW5kIGZvdXIgbW9kZXMgKFdhbGssIEN5Y2xlLCBIU1IsIENhcikuIFRvIGZhY2lsaXRhdGUgcGxvdHRpbmcsIHdlIGFkZCB0aGUgdGltZSB2YWx1ZXMgYW5kIHRoZW4gcmVzaGFwZSB0aGF0IDEwLWJ5LTQgbWF0cml4IGFzIGZvbGxvd3M6DQpgYGB7cn0NCnByb2JzIDwtIGRhdGEuZnJhbWUoc2lkZXdhbGtfZGVuc2l0eSA9IHNlcSgwLCA2MCwgNSksIHByb2JzKSAlPiUgDQogIGdhdGhlcihrZXkgPSAiTW9kZSIsIHZhbHVlID0gIlByb2JhYmlsaXR5IiwgLXNpZGV3YWxrX2RlbnNpdHkpDQpgYGANCg0KQnkgImdhdGhlcmluZyIgdGhlIHByb2JhYmlsaXRpZXMsIG5vdyB0aGUgZGF0YSBmcmFtZSBoYXMgb25lIGNvbHVtbiB3aXRoIHRoZSBtb2RlIGFuZCBvbmUgY29sdW1uIHdpdGggdGhlIHByb2JhYmlsaXR5LiBXZSBjYW4gdGhlbiBwbG90LCB1c2luZyBhIGRpZmZlcmVudCBjb2xvcnMgZm9yIGVhY2ggYWx0ZXJuYXRpdmU6DQpgYGB7cn0NCmdncGxvdChkYXRhID0gcHJvYnMsIGFlcyh4ID0gc2lkZXdhbGtfZGVuc2l0eSwgeSA9IFByb2JhYmlsaXR5LCBjb2xvciA9IE1vZGUpKSArDQogIGdlb21fbGluZSgpDQpgYGANCg0KV2UgY2FuIHNlZSB0aGF0IHRoZSBwcm9iYWJpbGl0eSBvZiB3YWxraW5nIChmb3IgYSB0cmlwIHRoYXQgdGFrZXMgdGhlIG1lZGlhbiBkdXJhdGlvbiBpbiB0aGUgc2FtcGxlKSB0ZW5kcyB0byBpbmNyZWFzZSBhcyB0aGUgZGVuc2l0eSBvZiBzaWRld2Fsa3MgaW5jcmVhc2UuIFRoZSBwcm9iYWJpbGl0eSBvZiB1c2luZyB0aGUgdGhyZWUgb3RoZXIgbW9kZXMgdGVuZHMgdG8gZGVjcmVhc2Ugd2l0aCBzaWRld2FsayBkZW5zaXR5LCBidXQgbW9yZSByYXBpZGx5IGZvciBIU1IgdGhhbiBmb3IgY2FyIG9yIGN5Y2xpbmcuDQoNCiMjIENvbXBhcmluZyBtb2RlbHM6IE1jRmFkZGVuJ3MgJFxyaG9eMiQNCg0KVGhlIGxvZy1saWtlbGlob29kIHJlcG9ydGVkIGluIHRoZSBzdW1tYXJ5IG9mIHRoZSBtb2RlbCBpcyB1c2VmdWwgYXMgYSBtZWFzdXJlIG9mIGdvb2RuZXNzIG9mIGZpdC4gUmVjYWxsIHRoYXQgdGhlIGxpa2VsaWhvb2Qgb2YgdGhpcyBtb2RlbCBpcyBib3VuZGVkIGJldHdlZW4gJDAkIGFuZCAkMSQsIGFuZCB0aGVyZWZvcmUgdGhlIGxvZy1saWtlbGlob29kIGlzIGJvdW5kZWQgYXQgdGhlIHVwcGVyIGVuZCBieSAkMCQgKGl0IGlzIG1pbnVzIGluZmluaXR5IGF0IHRoZSBsb3dlciBlbmQpLiBXZSBhbHNvIGtub3cgdGhhdCBoaWdoZXIgdmFsdWVzIG9mIHRoZSBsaWtlbGlob29kIHJlcHJlc2VudCBiZXR0ZXIgZml0cy4gDQoNCk9uZSBzaW1wbGUgZGlhZ25vc3RpYyB0byBjb21wYXJlIHRoZSBmaXQgb2YgbW9kZWxzIGlzIE1jRmFkZGVuJ3MgJFxyaG9eMiQuIFRoaXMgc3VtbWFyeSBkaWFnbm9zdGljIGlzIGRlZmluZWQgYXMgZm9sbG93czoNCiQkDQpccmhvXjIgPSAxIC0gXGZyYWN7bF4qfXtsXzB9DQokJA0Kd2hlcmUgJGxeKiQgaXMgdGhlIHZhbHVlIG9mIHRoZSBtYXhpbWl6ZWQgbG9nLWxpa2VsaWhvb2QgYW5kICRsXzAkIGlzIHRoZSB2YWx1ZSBvZiB0aGUgbG9nLWxpa2VsaWhvb2Qgb2YgYSBudWxsIG1vZGVsIChwZXJoYXBzIHdpdGhvdXQgY29uc3RhbnRzLCBvciBhIGNvbnN0YW50cyBvbmx5IG1vZGVsKS4gSWYgdGhlIG1vZGVsIGlzIHVuaW5mb3JtYXRpdmUsIGl0cyBsb2ctbGlrZWxpaG9vZCB3aWxsIHRlbmQgdG8gdGhlIGxpa2VsaWhvb2Qgb2YgdGhlIG51bGwgbW9kZWwuIEluIHRoaXMgY2FzZSAkbF4qL2xfMCQgdGVuZHMgdG8gb25lIGFuZCB0aGVyZWZvcmUgJFxyaG9eMiQgdGVuZHMgdG8gemVyby4gSWYgdGhlIG1heGltaXplZCBsb2ctbGlrZWxpaG9vZCBvZiB0aGUgbW9kZWwgdGVuZHMgdG8gMCAodGhlIHVwcGVyIGxpbWl0IGZvciB0aGUgbG9nLWxpa2VsaWhvb2QgZnVuY3Rpb24pLCAkXHJob14yJCB0ZW5kcyB0byBvbmUuDQoNCkFsdGhvdWdoICRccmhvXjIkIGlzIGJvdW5kZWQgYmV0d2VlbiB6ZXJvIGFuZCBvbmUsIGp1c3QgbGlrZSB0aGUgY29lZmZpY2llbnQgb2YgZGV0ZXJtaW5hdGlvbiAkUl4yJCBpbiByZWdyZXNzaW9uIGFuYWx5c2lzLCBpdHMgaW50ZXJwcmV0YXRpb24gaXMgX25vdF8gdGhlIHNhbWUgYXMgZm9yICRSXjIkLiBXaGVyZWFzICRSXjIkIGlzIGludGVycHJldGVkIGFzIHRoZSBwcm9wb3J0aW9uIG9mIHZhcmlhbmNlIGV4cGxhaW5lZCBieSB0aGUgbW9kZWwsICRccmhvXjIkIGxhY2tzIHN1Y2ggYW4gaW50ZXJwcmV0YXRpb24uIEFsc28sIHRoZSB2YWx1ZXMgb2YgJFxyaG9eMiQgdGVuZCB0byBiZSBsb3dlciwgYW5kIHZhbHVlcyBvZiAkMC40JCBhcmUgY29udmVudGlvbmFsbHkgY29uc2lkZXJlZCB2ZXJ5IGdvb2QgZml0cy4gVGhlIG1haW4gdXRpbGl0eSBvZiBNY0ZhZGRlbidzICRccmhvXjIkIGlzIGFzIGEgcXVpY2sgd2F5IG9mIGNvbXBhcmluZyB0aGUgcmVsYXRpdmUgZml0IG9mIGRpZmZlcmVudCBtb2RlbHMsIHJhdGhlciB0aGFuIGFzc2Vzc2luZyB0aGUgZml0IGFnYWluc3QgYW4gYWJzb2x1dGUgdmFsdWUgb2YgZ29vZG5lc3Mgb2YgZml0Lg0KDQojIyBDb21wYXJpbmcgbW9kZWxzOiB0aGUgbGlrZWxpaG9vZCByYXRpbyB0ZXN0DQoNCkFub3RoZXIgd2F5IHRvIGNvbXBhcmUgbW9kZWxzIGlzIGJ5IG1lYW5zIG9mIHRoZSBsaWtlbGlob29kIHJhdGlvIHRlc3QuIFRoaXMgdGVzdCBjb21wYXJlcyB0aGUgbG9nLWxpa2VsaWhvb2Qgb2YgdHdvIG1vZGVscyB0byBhc3Nlc3Mgd2hldGhlciB0aGV5IGFyZSBzaWduaWZpY2FudGx5IGRpZmZlcmVudC4gVGhlIHRlc3QgZm9sbG93cyB0aGUgJFxjaGleMiQgZGlzdHJpYnV0aW9uIHdpdGggZGVncmVlcyBvZiBmcmVlZG9tIGVxdWFsIHRvIHRoZSBkaWZmZXJlbmNlIGluIHRoZSBudW1iZXIgb2YgY29lZmZpY2llbnRzIGJldHdlZW4gdGhlIHR3byBtb2RlbHMuIFRoZSB0ZXN0IHJlcXVpcmVzIGEgYmFzZSBtb2RlbCBhbmQgYSBmdWxsIG1vZGVsLCBhbmQgdGhlIGJhc2UgbW9kZWwgbXVzdCBfbmVzdF8gd2l0aGluIHRoZSBmdWxsIG1vZGVsLiBOZXN0aW5nIGluIHRoaXMgc2Vuc2UgbWVhbnMgdGhhdCBmdWxsIG1vZGVsIG11c3QgYmUgcmVkdWNpYmxlIHRvIHRoZSBiYXNlIG1vZGVsIGJ5IHNldHRpbmcgc29tZSBjb2VmZmljaWVudHMgdG8gemVyby4NCg0KRm9yIGV4YW1wbGUsIGNvbnNpZGVyIHRoZSB1dGlsaXR5IGZ1bmN0aW9ucyBvZiBgbW9kZWwyYDoNCiQkDQpcYmVnaW57YXJyYXl9e2x9DQogIFZfe2lcdGV4dHtDeWNsZX19ID0gMCAmKyYgXGJldGFfMVx0ZXh0e3RpbWV9X3tpXHRleHR7Q3ljbGV9fSAmKyYgMFxcDQogIFZfe2lcdGV4dHtXYWxrfX0gPSBcbXVfe1x0ZXh0e1dhbGt9fSAmKyYgXGJldGFfMVx0ZXh0e3RpbWV9X3tpXHRleHR7V2Fsa319ICYrJiBcZ2FtbWFfezF9XHRleHR7c3dkfV97aX1cXA0KICBWX3tpXHRleHR7SFNSfX0gPSBcbXVfe1x0ZXh0e0hTUn19ICYrJiBcYmV0YV8xXHRleHR7dGltZX1fe2lcdGV4dHtIU1J9fSAmKyYgXGdhbW1hX3syfVx0ZXh0e3N3ZH1fe2l9XFwNCiAgVl97aVx0ZXh0e0hTUn19ID0gXG11X3tcdGV4dHtDYXJ9fSAmKyYgXGJldGFfMVx0ZXh0e3RpbWV9X3tpXHRleHR7Q2FyfX0gJismIFxnYW1tYV97M31cdGV4dHtzd2R9X3tpfVxcDQpcZW5ke2FycmF5fSAgDQokJA0KDQpXZSBjYW4gcmVkdWNlIHRoaXMgbW9kZWwgdG8gYG1vZGVsMWAgYnkgc2V0dGluZyAkXGdhbW1hX3sxfT1cZ2FtbWFfezJ9PVxnYW1tYV97M309MCQ6DQokJA0KXGJlZ2lue2FycmF5fXtsfQ0KICBWX3tpXHRleHR7Q3ljbGV9fSA9IDAgJismIFxiZXRhXzFcdGV4dHt0aW1lfV97aVx0ZXh0e0N5Y2xlfX1cXA0KICBWX3tpXHRleHR7V2Fsa319ID0gXG11X3tcdGV4dHtXYWxrfX0gJismIFxiZXRhXzFcdGV4dHt0aW1lfV97aVx0ZXh0e1dhbGt9fVxcDQogIFZfe2lcdGV4dHtIU1J9fSA9IFxtdV97XHRleHR7SFNSfX0gJismIFxiZXRhXzFcdGV4dHt0aW1lfV97aVx0ZXh0e0hTUn19XFwNCiAgVl97aVx0ZXh0e0hTUn19ID0gXG11X3tcdGV4dHtDYXJ9fSAmKyYgXGJldGFfMVx0ZXh0e3RpbWV9X3tpXHRleHR7Q2FyfX1cXA0KXGVuZHthcnJheX0gIA0KJCQNCg0KSW4gdGhpcyB3YXksIGBtb2RlbDFgICJuZXN0cyIgaW4gYG1vZGVsMmAuDQoNCkluIHRoZSBzdW1tYXJ5IG9mIHRoZSBtb2RlbHMsIHRoZSBsaWtlbGlob29kIHJhdGlvIHRlc3QgaXMgcmVwb3J0ZWQuIFNlZToNCmBgYHtyfQ0Kc3VtbWFyeShtb2RlbDEpDQpgYGANCg0KVGhlIHRlc3QgcmVwb3J0ZWQgaW4gdGhlIG91dHB1dCBvZiB0aGUgbW9kZWwgaXMgYWdhaW5zdCB0aGUgbnVsbCBtb2RlbCwgdGhhdCBpcywgYSBtb2RlbCB3aXRoIG5vIHZhcmlhYmxlcyBhdCBhbGwuIFRoaXMgaXMgdGhlIGxlYXN0IGluZm9ybWF0aXZlIG9mIGFsbCBtb2RlbHMuDQoNCldoZW4gdHdvIG5vbi1udWxsIG1vZGVscyBuZWVkIHRvIGJlIGNvbXBhcmVkLCB0aGUgYGxydGVzdGAgZnVuY3Rpb24gaW1wbGVtZW50cyB0aGUgbGlrZWxpaG9vZCByYXRpbyB0ZXN0IGZvciB0d28gaW5wdXRzLCB3aGljaCBhcmUgdHdvIGBtbG9naXRgIG1vZGVscywgYXMgZm9sbG93czoNCmBgYHtyfQ0KbHJ0ZXN0KG1vZGVsMSwgbW9kZWwyKQ0KYGBgDQoNCk5vdGljZSB0aGF0IHRoZSBudW1iZXIgb2YgZGVncmVlcyBvZiBmcmVlZG9tIChEZikgaXMgJDMkOiB0aGlzIGlzIGJlY2F1c2UgdGhlcmUgYXJlIHRocmVlIGluZGl2aWR1YWwtc3BlY2lmaWMgcGFyYW1ldGVycyBpbiBgbW9kZWwyYCB0aGF0IGFyZSBub3QgcHJlc2VudCBpbiBgbW9kZWwxYC4gVGhlIG51bGwgaHlwb3RoZXNpcyBvZiB0aGUgdGVzdCBpcyB0aGF0IHRoZSBsb2ctbGlrZWxpaG9vZCBvZiB0aGUgdHdvIG1vZGVscyBpcyBub3QgZGlmZmVyZW50LCBpbiBvdGhlciB3b3JkcywgdGhhdCB0aGUgYWx0ZXJuYXRlIG1vZGVsIGlzIG5vdCBhbiBpbXByb3ZlbWVudCBvdmVyIHRoZSBiYXNlIG1vZGVsLg0KDQpJbiB0aGUgcHJlc2VudCBjYXNlLCB0aGUgdmVyeSBzbWFsbCAkcCQtdmFsdWUgbGVhZHMgdXMgdG8gcmVqZWN0IHRoZSBudWxsIGh5cG90aGVzaXMsIGFuZCB0aGUgY29uY2x1c2lvbiBpcyB0aGF0IGBtb2RlbDJgLCB3aGljaCBpbmNsdWRlcyBhZ2UsIGlzIGEgc2lnbmlmaWNhbnQgaW1wcm92ZW1lbnQgb3ZlciBgbW9kZWwxYCwgd2hpY2ggZG9lcyBub3QuDQoNCiMjIEV4ZXJjaXNlDQoNCjEuIEluIHRoZSBleGFtcGxlIGluIHRoaXMgY2hhcHRlciB3ZSBlc3RpbWF0ZWQgdGhlIHByb2JhYmlsaXRpZXMgb2YgY2hvb3NpbmcgZGlmZmVyZW50IG1vZGVzIGJ5IHNpZGV3YWxrIGRlbnNpdHkgc2V0dGluZyB0cmF2ZWwgdGltZSB0byB0aGUgaW4tc2FtcGxlIG1lZGlhbi4gVXNlIGBtb2RlbDJgIHRvIGNhbGN1bGF0ZSB0aGUgcHJvYmFiaWxpdHkgb2YgY2hvb3NpbmcgZWFjaCBtb2RlIGJ1dCBub3cgZm9yIHRyYXZlbCB0aW1lcyAyMCwgMzAsIDQwLiBEaXNjdXNzIHRoZSByZXN1bHRzLg0KDQoyLiBFc3RpbWF0ZSBhIG1vZGVsIHVzaW5nIGZvcm11bGEgYGYzYCAoY2FsbCBpdCBgbW9kZWwzYCkuIERpc2N1c3MgdGhlIG91dHB1dCBvZiB0aGlzIG1vZGVsLg0KDQozLiBVc2UgdGhlIGxpa2VsaWhvb2QgcmF0aW8gdGVzdCB0byBjb21wYXJlIGBtb2RlbDNgIHRvIGBtb2RlbDFgLg0KDQo0LiBDYW4geW91IHVzZSB0aGUgbGlrZWxpaG9vZCByYXRpbyB0ZXN0IHRvIGNvbXBhcmUgYG1vZGVsM2AgdG8gYG1vZGVsMmA/IERpc2N1c3Mu